Tables & pagination

The core of every BMS index screen. One .table base, opt-in modifiers for stripes, density, lines, sticky headers, sorting, and selection — plus the pagination vocabulary that lives in card footers.

Anatomy

Service Environment Status Uptime Actions
bms-cloud production running 99.98%
bms-audit production running 99.95%
bms-portal staging degraded 97.20%
bms-cups production running 99.99%
bms-docs development stopped —
<div class="table-wrap">
  <table class="table table-hover">
    <thead><tr><th>Service</th>…</tr></thead>
    <tbody>
      <tr>
        <td class="table-lead">bms-cloud</td>
        <td><span class="badge badge-success">running</span></td>
      </tr>
    </tbody>
  </table>
</div>

Index chrome

.grid-header · .grid-select · .grid-pager

The anatomy every index screen repeats (D-E, D-F). Two toolbar rows — primary actions left with a menu button right, then the filter toggle left with Find / Columns / Export right — a selection column, and a three-zone pager whose controls match the row height. Seen assembled on the work queue.

Status CodeNameBalance
! 1042 Corinda Metals 12,405.75
✓ 1049 Eastvale Dairy 2,215.20
! 1056 Harlow Signage 0.00

1 to 3 of 3

Striped rows

.table-striped
Backup jobScheduleSize
cloud-db-primary hourly 1.2 GB
audit-db daily 480 MB
portal-uploads daily 6.8 GB
ops-config weekly 12 MB
cups-spool daily 220 MB

The density tiers

.table-data is the work-surface default

bms-cloud grids render 32px rows (measured live, 16 Aug 2026) and fit 20 of them on a default page. At the comfortable default a work queue loses about 30% of its rows — a workflow regression dressed as a redesign. Density is the product's core value to the people who use it all day, so any screen replacing a jqGrid or a left rail ships .table-data, not .table.

TierPadding Row height vs legacyUse for
.table py-3~45px1.4× Comfortable — settings, summaries, anything not lived in
.table-dense py-2~37px1.16× Compact — supporting tables inside a card
.table-data py-1.532pxparity Work queues and anything displacing a jqGrid

The arithmetic: text-sm sets a 20px line box and py-1.5 adds 6px top and bottom. Same height as legacy, reached with 14px Inter rather than 14px Roboto Condensed — which is why type and density had to be budgeted together, since Inter sets wider at equal size. tabular-nums is part of the tier, not a nicety: aged-debt buckets only scan as columns when the digits line up.

CustomerRef Current1–14 15–3031–90 90+
Heinz Australia ACC-10428 12,480.00 0.00 1,240.00 0.00 0.00
Kellogg's ANZ ACC-10431 3,905.50 880.00 0.00 0.00 0.00
Simplot Foods ACC-10440 27,110.25 4,010.00 2,880.15 990.00 0.00
Goodman Fielder ACC-10455 0.00 0.00 0.00 1,150.00 6,420.80
Mondelez Pacific ACC-10462 8,220.00 0.00 0.00 0.00 0.00

Dense with vertical lines

.table-dense.table-lined
QueueReadyBusyFailed
default 412 8 0
mailers 36 2 0
exports 9 4 1
webhooks 128 12 3
low 1043 1 0

Sortable headings, sticky header

.table-sticky + .table-sort
app-01 syd-1 34% 61%
app-02 syd-1 41% 58%
app-03 syd-2 12% 44%
db-01 syd-1 68% 83%
db-02 syd-2 22% 71%
jobs-01 syd-1 55% 49%
jobs-02 syd-2 47% 52%
proxy-01 syd-1 9% 31%
proxy-02 syd-2 11% 29%
cache-01 syd-1 18% 76%

Selectable rows

2 selected
User Role Last sign-in
Alex Rivera admin 2 hours ago
Sam Whitford operator yesterday
Jo Tanaka operator 3 days ago
Priya Nair viewer last week

Grouped rows with summary

.table-group-row + tfoot
Line itemEnvironmentMonthly cost
Compute
App servers ×4production$412.00
Job runners ×2production$186.00
Storage
Database volumesproduction$94.50
Backup archiveall$31.20
Total$723.70

Pagination

Card footer with page buttons

Showing 21–30 of 112

Centered page numbers

Simple footer

Page 2 of 8

<nav class="pagination" aria-label="Pagination">
  <a href="#" class="page-btn">1</a>
  <a href="#" class="page-btn" aria-current="page">2</a>
  <span class="page-gap">…</span>
  <a href="#" class="page-btn">8</a>
</nav>