Tables

Base data table, sortable column headers, and filter toolbar patterns used across the admin console and arXiv Check.

The base table

The .ds-table class provides warm-tinted header row, subtle row dividers, and hover highlighting. Condensed uppercase headers use IBM Plex Sans Condensed.

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="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. Apply it to the <table> element: it gives the header row its tint, the rows their dividers, and each row its hover highlight.
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.
.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.

The framed table

The public site's table, .ds-table-framed in tier 1: the page's own type, a hairline frame and rounded corners, header and cells taking the element defaults. It is the table on every specification section of this documentation. Shown here beside the internal data table above so the two can be compared: they differ in type size, in the header row, and in row hover.

DateTitleCategory
2026-03-24Spectral gaps in random regular hypergraphsmath.CO
2026-03-23Anomalous diffusion in confined active mattercond-mat.soft
2026-03-22A note on the endpoint Strichartz estimatemath.AP
Relevant code
<table class="ds-table-framed">
  <thead>
    <tr><th scope="col">Date</th><th scope="col">Title</th><th scope="col">Category</th></tr>
  </thead>
  <tbody>
    <tr><td>2026-03-24</td><td>Spectral gaps in random regular hypergraphs</td><td>math.CO</td></tr>
  </tbody>
</table>
.ds-table-framed
Tier 1. The frame: a hairline border, 8px radius, the surface colour. Type, header and cell padding come from the element defaults every table inside .ds-page has.
.ds-table
Tier 2. The internal data table above: smaller type, a tinted condensed uppercase header row, row dividers and row hover. The two are separate classes today.

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, click to sort

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. Unsorted it shows ⇅ in --ds-border-strong (3.12:1 on the header background).
.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.

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" style="margin-bottom: var(--ds-space-3)">
  <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="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.

Bulk actions and row selection

Rules for tables whose rows can be selected and acted on together — moderation queues, ownership lists.

Selection active — actions left, filter right, selected rows tinted

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 ds-btn-group--split" style="margin-bottom: var(--ds-space-3)">
  <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" style="margin-inline-start: var(--ds-space-tight)">Remove…</button>
    <span class="ds-bulk-count" role="status">2 selected · <a href="#">Clear selection</a></span>
  </div>
  <div class="ds-filter">
    <label for="filter-owned">Show</label>
    <select id="filter-owned">…</select>
  </div>
</div>
<div class="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>
        <td>
          <input type="checkbox" id="sel-2">
          <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>
.ds-btn-group--split
The bulk-action bar: one group at each end. The action buttons and their descriptive text sit at the start, the filter at the end. The bar has no fill of its own.
.ds-btn-primary, .ds-btn, .ds-btn-destructive
The actions, in the tiers documented on Buttons. The destructive action comes last and stands apart from the constructive ones.
.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.
.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 carries 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.

Nothing selected — actions disable, they never disappear

0 selected
Relevant code
<div class="ds-btn-group ds-btn-group--split">
  <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 style="margin-inline-start: var(--ds-space-tight)">Remove…</button>
    <span class="ds-bulk-count" role="status">0 selected</span>
  </div>
  <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

  • Mark the header cells. Put scope="col" on every <th> in the header row, so a screen reader reads the column name with each cell instead of a bare value.
  • Give the table a name. A <caption>, or an aria-label on the scrolling region around the table, as the examples on this page do. Without one, a screen reader user lands in “table, 4 columns” with no idea which table it is.
  • Say which column is sorted. Put aria-sort="ascending" or aria-sort="descending" on the sorted <th> and remove it from the others when the sort changes. The arrow is a glyph, and the class is invisible to a screen reader.
  • Label every row checkbox. Each checkbox gets a <label class="is-sr-only"> that names its row, such as “Select Asymptotic Expansions”. Without it the choice reads as “checkbox, not checked” and nothing more. Prefer the label to aria-label, because page translation tools skip attributes.
  • Announce the count. The selection count sits in an element with role="status", so a screen reader user hears “2 selected” when it changes without leaving the checkbox they just ticked.
  • Actions disable, they never disappear. When nothing is selected, bulk-action buttons render disabled — not removed. Controls that appear and vanish cost users their bearings. This rule is universal (it also appears in the agent guide’s hard rules).

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 and their descriptive text (selection count, “Clear selection”) sit on the left; filter dropdowns sit on the right. No background tint behind the action buttons — the bar itself stays neutral. Destructive actions (Remove, Delete) are visually separated from constructive ones and always require a confirmation step.