Tables

The table and what can be built on it: sortable columns, a filter toolbar, row selection and bulk actions. All of it is shared by the public site and internal tools; internal tools change only the colours.

The table

One table, .ds-table, on the public site and in internal tools. It has row dividers and a row hover. The header row comes from the markup: a table with a <thead> has one, in condensed uppercase body-colour text on the warm tint, and a table without one does not.

With a header row

Note

The Type column uses rectangular tags at the default level, documented on Tags.

Date Title Submitter Type
2026-03-24 Asymptotic Expansions of Schrödinger Operators Esther McGill-Henry New
2025-07-09 Symmetry Breaking in Non-Abelian Gauge Theories Esther McGill-Henry Rep
2024-07-08 Spectral Properties of Discrete Schrödinger Operators Esther McGill-Henry Wdr
Relevant code
<div class="ds-table-scroll" role="region" aria-label="Submissions" tabindex="0">
  <table class="ds-table">
    <thead>
      <tr>
        <th scope="col">Date</th>
        <th scope="col">Title</th>
        <th scope="col">Submitter</th>
        <th scope="col">Type</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>2026-03-24</td>
        <td>Asymptotic Expansions of Schrödinger Operators</td>
        <td>Esther McGill-Henry</td>
        <td><span class="ds-tag ds-tag--rectangle">New</span></td>
      </tr>
      <tr>
        <td>2025-07-09</td>
        <td>Symmetry Breaking in Non-Abelian Gauge Theories</td>
        <td>Esther McGill-Henry</td>
        <td><span class="ds-tag ds-tag--rectangle">Rep</span></td>
      </tr>
      <!-- … one row per record -->
    </tbody>
  </table>
</div>
.ds-table
The whole table, on the <table> element: the frame, the row dividers and the row hover. Directly inside a card it has no frame of its own; the card is the frame.
<thead>
Draws the header row. Leave it out and the table has none; no class turns the header row on or off.
style="table-layout: fixed"
Add it inline on the <table> when fixed column widths are needed. The table defaults to auto layout.
.ds-table-footer
A <div> below the table for row counts or pagination.
.ds-tag--rectangle
The Type column: a rectangular tag at the default level, one per row, documented on Tags. No colour is mapped to New, Rep or Wdr.
scope="col"
Required on every <th> in the header row, so a screen reader reads the column name with each cell.
.ds-table-scroll
The wrapper. A table wider than the viewport scrolls inside it, so the page never scrolls sideways. It takes role="region", an aria-label that names the table, and tabindex="0" so a keyboard user can reach it to scroll.

With a header column

Primary categorymath.SP
Submitted2026-03-24
LicenseCC BY 4.0
Relevant code
<table class="ds-table">
  <tbody>
    <tr><th scope="row">Primary category</th><td>math.SP</td></tr>
    <tr><th scope="row">Submitted</th><td>2026-03-24</td></tr>
    <tr><th scope="row">License</th><td>CC BY 4.0</td></tr>
  </tbody>
</table>
scope="row"
A <th> in the body is a row header: it names its row, in the cell type at full weight on a tinted ground, and a screen reader reads it with each cell in the row. The tint comes from the element, so no class is needed.

Outside borders

A table has a border all the way round, except directly inside a card, where the card is the frame and the table has none.

Directly inside a card

DateTitle
2026-03-24Asymptotic Expansions of Schrödinger Operators
2025-07-09Symmetry Breaking in Non-Abelian Gauge Theories

Not directly inside a card

DateTitle
2026-03-24Asymptotic Expansions of Schrödinger Operators
2025-07-09Symmetry Breaking in Non-Abelian Gauge Theories

Sortable column headers

A sortable column has a <button class="sortable"> inside its <th>, holding the label and a <span class="sort-arrow"> for the direction. It looks like the plain header and it can be reached and pressed from the keyboard, which a <th> on its own cannot. .sort-asc or .sort-desc and aria-sort go on the <th> of the sorted column.

Sortable headers

Note

Sort logic is application-specific; the CSS only handles visual states.

Title
2026-03-24 Asymptotic Expansions New On Hold
2025-07-09 Symmetry Breaking Rep Removed
2024-07-08 Spectral Properties New Removed
2023-04-18 Geometric Phases Wdr Removed
Relevant code
<table class="ds-table">
  <thead>
    <tr>
      <th scope="col" class="sort-desc" aria-sort="descending">
        <button type="button" class="sortable">Date <span class="sort-arrow">↓</span></button>
      </th>
      <th scope="col">Title</th>
      <th scope="col"><button type="button" class="sortable">Type <span class="sort-arrow">⇅</span></button></th>
      <th scope="col"><button type="button" class="sortable">Status <span class="sort-arrow">⇅</span></button></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>2026-03-24</td>
      <td>Asymptotic Expansions</td>
      <td><span class="ds-tag ds-tag--rectangle">New</span></td>
      <td>On Hold</td>
    </tr>
    <tr>
      <td>2025-07-09</td>
      <td>Symmetry Breaking</td>
      <td><span class="ds-tag ds-tag--rectangle">Rep</span></td>
      <td>Removed</td>
    </tr>
    <!-- … one row per record -->
  </tbody>
</table>
<div class="ds-table-footer">1–4 of 4</div>
.sortable
On a <button type="button"> inside the <th>. The button is styled to look like the plain header text, so nothing changes visually; what it adds is a keyboard stop, so a reader who cannot use a mouse can sort. Pointer cursor, and the label darkens on hover.
.sort-arrow
The direction indicator, a <span> inside the header. The glyph in the markup is replaced by an icon: two arrows in --ds-text-muted when the column is unsorted.
.sort-asc
On the <th> of the sorted column. The arrow shows ↑ in --ds-text.
.sort-desc
On the <th> of the sorted column. The arrow shows ↓ in --ds-text.
aria-sort="ascending", aria-sort="descending"
Required on the sorted <th>, matching the class. Remove it from the column that is no longer sorted. The arrow is a glyph, so this is what tells a screen reader which column orders the table.
.ds-table-footer
The row count under the table.

Filter toolbar

The .ds-filter component provides a minimal underline-only <select> for filtering data. Typically placed in a toolbar row above or alongside a table. The custom chevron replaces the native dropdown arrow for visual consistency. To narrow a whole view, use the filter bar.

Filter select, standalone

Relevant code
<div class="ds-filter">
  <label for="filter-status">Show:</label>
  <select id="filter-status">
    <option>All</option>
    <option selected>Removed</option>
    <option>On Hold</option>
  </select>
</div>
.ds-filter
The wrapper. Holds one <label> and one <select>. The select gets an underline-only border, the custom chevron, and a focus ring in --ds-focus-ring. Always include an “All” option as the inclusive default.
<label for>, <select id>
Required. The visible label is the select’s name: the for attribute points at the select’s id.
.ds-btn-group
The container for two or more filters side by side, the same one any set of related controls takes.

Filter above a table

Name Category Status
Alice Chen cs.AI Active
Bob Singh math.AP Archived
Relevant code
<div class="ds-btn-group ds-btn-group--end">
  <div class="ds-filter">
    <label for="filter-users">Show:</label>
    <select id="filter-users">
      <option selected>All</option>
      <option>Active</option>
      <option>Archived</option>
    </select>
  </div>
</div>
<div class="ds-table-scroll" role="region" aria-label="Users" tabindex="0">
  <table class="ds-table">
    …
  </table>
</div>
.ds-btn-group--end
Pushes the filter to the trailing edge of the toolbar row, where filters sit.

Filter bar

A row of fields that narrows everything below it, with one Apply button. It is a <form method="get">, so the choices go in the page address and a filtered view can be linked.

Settings
Clear
CategoryNew submissionsOpen holds
cond-mat.str-el2146
cond-mat.soft1322
Relevant code
<form class="ds-filter-bar" method="get" action="tables.html#filter-bar" aria-label="Filters">
  <div class="ds-filter-bar-group">
    <div class="ds-field">
      <label class="ds-label" for="fb-group">Group</label>
      <select class="ds-input" id="fb-group" name="group">
        <option>Physics</option>
        <option>Mathematics</option>
        <option>Computer Science</option>
      </select>
    </div>
    <div class="ds-field">
      <label class="ds-label" for="fb-archive">Archive</label>
      <select class="ds-input" id="fb-archive" name="archive">
        <option>All archives</option>
        <option>cond-mat</option>
        <option>hep-th</option>
      </select>
    </div>
    <div class="ds-field">
      <label class="ds-label" for="fb-category">Category</label>
      <select class="ds-input" id="fb-category" name="category">
        <option>All categories</option>
        <option>cond-mat.str-el</option>
        <option>cond-mat.soft</option>
      </select>
    </div>
    <div class="ds-field">
      <label class="ds-label" for="fb-from">From</label>
      <input class="ds-input" id="fb-from" name="from" type="date" value="2026-09-01">
    </div>
    <div class="ds-field">
      <label class="ds-label" for="fb-to">To</label>
      <input class="ds-input" id="fb-to" name="to" type="date" value="2026-09-30">
    </div>
  </div>
  <fieldset class="ds-filter-bar-group ds-filter-bar-group--settings">
    <legend class="is-sr-only">Settings</legend>
    <div class="ds-field">
      <label class="ds-label" for="fb-threshold">Threshold days</label>
      <input class="ds-input" id="fb-threshold" name="threshold" type="number" min="1" value="7">
    </div>
  </fieldset>
  <div class="ds-filter-bar-actions">
    <a href="tables.html#filter-bar">Clear</a>
    <button class="ds-btn ds-btn-primary" type="submit">Apply</button>
  </div>
</form>
.ds-filter-bar
The <form method="get">, on the warm tint, with an aria-label such as "Filters". It narrows everything below it. One per view. The fields sit in equal columns, as many as fit the width.
.ds-filter-bar-group
The filters. Each field takes a column. The fields are the standard .ds-field, .ds-label and .ds-input, with labels above.
.ds-filter-bar-group--settings
Optional. A <fieldset> whose <legend> is .is-sr-only, so it is read out and takes no space. It starts its own row, for a setting: a number that changes a calculation, such as threshold days. A setting is not a filter.
.ds-filter-bar-actions
Apply, a submit button, and an optional Clear link to the address without filters. They sit at the end of the last row.

Compact: one row

Relevant code
<form class="ds-filter-bar" method="get" action="tables.html#filter-bar" aria-label="Subject">
  <div class="ds-filter"><label for="cfb-group">Group</label><select id="cfb-group" name="group"><option>cs</option><option>math</option></select></div>
  <div class="ds-filter"><label for="cfb-archive">Archive</label><select id="cfb-archive" name="archive"><option>cs</option></select></div>
  <div class="ds-filter"><label for="cfb-cat">Category</label><select id="cfb-cat" name="category"><option>All</option></select></div>
  <button class="ds-btn" type="submit">Apply</button>
</form>
.ds-filter-bar with .ds-filter
A filter bar whose filters are the compact .ds-filter controls lays them out on one row, with less padding. Use it where a full filter bar would push the content down, such as beside navigation. Apply is a secondary .ds-btn, sized to the row.

Compact: a date range

Relevant code
<form class="ds-filter-bar" method="get" action="tables.html#filter-bar" aria-label="Dates">
  <div class="ds-filter"><label for="cfb-start">Start</label><input id="cfb-start" name="start" type="date" value="2026-09-16"></div>
  <div class="ds-filter"><label for="cfb-end">End (inclusive)</label><input id="cfb-end" name="end" type="date" value="2026-10-06"></div>
  <button class="ds-btn" type="submit">Apply</button>
</form>
.ds-filter with a date input
A compact filter can hold a date as well as a select. The date keeps its own width.

Row selection

Rows a reader can choose, one checkbox per row, so an action can apply to several at once.

Select Date Title Type
2026-03-24 Asymptotic Expansions of Schrödinger Operators New
2025-07-09 Symmetry Breaking in Non-Abelian Gauge Theories Rep
2024-07-08 Spectral Properties of Discrete Schrödinger Operators Wdr
Relevant code
<div class="ds-table-scroll" role="region" aria-label="Owned papers" tabindex="0">
  <table class="ds-table">
    <thead>
      <tr>
        <th scope="col"><span class="is-sr-only">Select</span></th>
        <th scope="col">Date</th>
        <th scope="col">Title</th>
        <th scope="col">Type</th>
      </tr>
    </thead>
    <tbody>
      <tr class="is-selected">
        <td>
          <input type="checkbox" id="sel-1" checked>
          <label for="sel-1" class="is-sr-only">Select Asymptotic Expansions of Schrödinger Operators</label>
        </td>
        <td>2026-03-24</td>
        <td>Asymptotic Expansions of Schrödinger Operators</td>
        <td><span class="ds-tag ds-tag--rectangle">New</span></td>
      </tr>
      <tr class="is-selected">
        <td>
          <input type="checkbox" id="sel-2" checked>
          <label for="sel-2" class="is-sr-only">Select Symmetry Breaking in Non-Abelian Gauge Theories</label>
        </td>
        <td>2025-07-09</td>
        <td>Symmetry Breaking in Non-Abelian Gauge Theories</td>
        <td><span class="ds-tag ds-tag--rectangle">Rep</span></td>
      </tr>
      <!-- … one row per record -->
    </tbody>
  </table>
</div>
.is-selected
On the <tr>. Tints the row with --ds-accent-wash; the tint aids scanability of the selection set. Selection state must not rely on the tint alone: the row’s checkbox shows the state (WCAG 1.4.1).
<input type="checkbox">
The first cell of every row. A direct child of the <td>, which gives it a 24px target and this surface’s accent. Each one has a <label class="is-sr-only"> that names its row.

Bulk actions

Actions that apply to every selected row at once, as in moderation queues and ownership lists. The bar sits above a table with row selection.

Selection active

2 selected · Clear selection
Select Date Title Type
2026-03-24 Asymptotic Expansions of Schrödinger Operators New
2025-07-09 Symmetry Breaking in Non-Abelian Gauge Theories Rep
2024-07-08 Spectral Properties of Discrete Schrödinger Operators Wdr
Relevant code
<div class="ds-btn-group">
  <button class="ds-btn ds-btn-primary" type="button">Approve</button>
  <button class="ds-btn" type="button">Hold</button>
  <button class="ds-btn ds-btn-destructive" type="button">Remove…</button>
  <span class="ds-bulk-count" role="status">2 selected · <a href="#">Clear selection</a></span>
  <div class="ds-filter">
    <label for="filter-owned">Show</label>
    <select id="filter-owned">…</select>
  </div>
</div>
<div class="ds-table-scroll" role="region" aria-label="Owned papers" tabindex="0">
  <table class="ds-table">…</table>
</div>
.ds-btn-group
The bulk-action bar: the actions, then the selection count, with any filter at the far end of the row. It wraps on a narrow screen. The bar has no fill of its own. Directly above a table it keeps a gap from it.
.ds-btn-primary, .ds-btn, .ds-btn-destructive
The actions, in the tiers documented on Buttons. The destructive action comes last.
.ds-bulk-count
The selection count and the “Clear selection” link, beside the actions. Takes role="status" so a change in the count is announced.

Nothing selected

0 selected
Relevant code
<div class="ds-btn-group">
  <button class="ds-btn ds-btn-primary" type="button" disabled>Approve</button>
  <button class="ds-btn" type="button" disabled>Hold</button>
  <button class="ds-btn ds-btn-destructive" type="button" disabled>Remove…</button>
  <span class="ds-bulk-count" role="status">0 selected</span>
  <div class="ds-filter">
    <label for="filter-owned">Show</label>
    <select id="filter-owned">…</select>
  </div>
</div>
<button disabled>
The disabled attribute on every action button while nothing is selected. The buttons stay in place; the filter stays live, because it does not depend on a selection.

Accessibility essentials

Modifiers

Striped rows

Every other row takes a tint, which helps the eye follow a long row across a wide table.

DateTitleType
2026-03-24Asymptotic Expansions of Schrödinger OperatorsNew
2025-07-09Symmetry Breaking in Non-Abelian Gauge TheoriesRep
2024-07-08Spectral Properties of Discrete Schrödinger OperatorsWdr
2023-04-18Geometric Phases in Adiabatic TransportNew
Relevant code
<table class="ds-table ds-table--striped">
  …
</table>
.ds-table--striped
Tints every even row of the body in Warm Tint, on both surfaces.

Numeric columns

Numbers align on the right, and their digits line up from row to row.

CategoryNew submissionsOpen holds
cs.AI3,96148
cs.LG2,370112
cs.CL1,3217
Relevant code
<th scope="col" class="ds-num">New submissions</th>
…
<td class="ds-num">3,961</td>
.ds-num
On every cell of a numeric column, the header included. Aligns the numbers on the right and uses tabular figures.

Rules

Sort interaction

Pressing a sortable header, by mouse or keyboard, toggles through: descending → ascending → descending. Clicking a different column resets the previous column to unsorted. Sort logic is application-specific; the CSS only handles visual states.

Bulk-action bar layout

Action buttons come first, then their descriptive text (selection count, “Clear selection”), with any filter at the far end of the row. No background tint behind the action buttons — the bar itself stays neutral. Destructive actions (Remove, Delete) come last among the actions and always require a confirmation step.