NETFRAME_DOCS // TABLE
GITHUB

Table

Data tables. Wrap in .nf-table-wrap to handle horizontal overflow on small screens.

Basic

DEFAULT TABLE

NODE_001ONLINE12ms99.9%
NODE_002IDLE8ms100%
NODE_003OFFLINE—0%
<div class="nf-table-wrap">
<table class="nf-table">
  <thead>
    <tr>
      <th scope="col"><button class="nf-table-sort" type="button" aria-label="NODE_ID">NODE_ID</button></th>
      <th scope="col"><button class="nf-table-sort" type="button" aria-label="STATUS">STATUS</button></th>
      <th scope="col"><button class="nf-table-sort" type="button" aria-label="LATENCY">LATENCY</button></th>
      <th scope="col"><button class="nf-table-sort" type="button" aria-label="UPTIME">UPTIME</button></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>NODE_001</td>
      <td><span class="nf-badge nf-badge-primary">ONLINE</span></td>
      <td>12ms</td>
      <td>99.9%</td>
    </tr>
    <tr>
      <td>NODE_002</td>
      <td><span class="nf-badge nf-badge-ghost">IDLE</span></td>
      <td>8ms</td>
      <td>100%</td>
    </tr>
    <tr>
      <td>NODE_003</td>
      <td><span class="nf-badge nf-badge-error">OFFLINE</span></td>
      <td>—</td>
      <td>0%</td>
    </tr>
  </tbody>
</table>
</div>

Striped rows

STRIPED

NETD10420.1%12MB
ICE_CORE10782.3%84MB
UPLINK10910.8%32MB
SCAN_SVC11044.1%128MB
<div class="nf-table-wrap">
<table class="nf-table nf-table-striped">
  <thead>
    <tr>
      <th scope="col"><button class="nf-table-sort" type="button" aria-label="PROCESS">PROCESS</button></th>
      <th scope="col"><button class="nf-table-sort" type="button" aria-label="PID">PID</button></th>
      <th scope="col"><button class="nf-table-sort" type="button" aria-label="CPU">CPU</button></th>
      <th scope="col"><button class="nf-table-sort" type="button" aria-label="MEM">MEM</button></th>
    </tr>
  </thead>
  <tbody>
    <tr><td>NETD</td><td>1042</td><td>0.1%</td><td>12MB</td></tr>
    <tr><td>ICE_CORE</td><td>1078</td><td>2.3%</td><td>84MB</td></tr>
    <tr><td>UPLINK</td><td>1091</td><td>0.8%</td><td>32MB</td></tr>
    <tr><td>SCAN_SVC</td><td>1104</td><td>4.1%</td><td>128MB</td></tr>
  </tbody>
</table>
</div>

Row states

ACTIVE AND DANGER ROWS

NODE_001NOMINAL—
NODE_002SELECTEDPROCESSING
NODE_003CRITICALQUARANTINE
<div class="nf-table-wrap">
<table class="nf-table">
  <thead>
    <tr>
      <th scope="col"><button class="nf-table-sort" type="button" aria-label="NODE_ID">NODE_ID</button></th>
      <th scope="col"><button class="nf-table-sort" type="button" aria-label="STATUS">STATUS</button></th>
      <th scope="col"><button class="nf-table-sort" type="button" aria-label="ACTION">ACTION</button></th>
    </tr>
  </thead>
  <tbody>
    <tr><td>NODE_001</td><td>NOMINAL</td><td>—</td></tr>
    <tr class="nf-table-row-active"><td>NODE_002</td><td>SELECTED</td><td>PROCESSING</td></tr>
    <tr class="nf-table-row-danger"><td>NODE_003</td><td>CRITICAL</td><td>QUARANTINE</td></tr>
  </tbody>
</table>
</div>

JavaScript

Every demo above supports header sorting with a mouse, touch, Enter or Space. NetFrame supplies the styles; this optional controller sorts rows and updates aria-sort. Numeric values (including ms, %, MB and PIDs) sort numerically; missing values stay last. Sorting does not change the underlying dataset.

document.querySelectorAll('.nf-table').forEach((table) => {
  const headers = [...table.querySelectorAll('thead th')];
  const body = table.querySelector('tbody');
  if (!body) return;
  const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
  const value = (row, column) => row.cells[column].textContent.trim();
  const missing = (text) => text === '' || text === '—';
  const numeric = (text) => /^-?\d+(?:\.\d+)?(?:ms|%|MB)?$/i.test(text);
  headers.forEach((header, column) => {
    const button = header.querySelector('.nf-table-sort');
    if (!button) return;
    button.addEventListener('click', () => {
      const direction = header.getAttribute('aria-sort') === 'ascending' ? -1 : 1;
      const rows = [...body.rows].sort((a, b) => {
        const x = value(a, column);
        const y = value(b, column);
        if (missing(x) || missing(y)) return Number(missing(x)) - Number(missing(y));
        const order = numeric(x) && numeric(y)
          ? parseFloat(x) - parseFloat(y)
          : collator.compare(x, y);
        return order * direction;
      });
      headers.forEach((other) => other.removeAttribute('aria-sort'));
      header.setAttribute('aria-sort', direction === 1 ? 'ascending' : 'descending');
      body.append(...rows);
    });
  });
});

Class reference

Class Description
nf-table-wrap Overflow wrapper — apply to a <div> around the table
nf-table Table element — full width, collapsed borders
nf-table-sort Header button — optional sorting controller uses aria-sort on its <th>
nf-table-striped Alternates even row background
nf-table-row-active Green left border accent — selected row
nf-table-row-danger Error left border accent — critical row