Table
Data tables. Wrap in .nf-table-wrap to handle horizontal overflow on small screens.
Basic
DEFAULT TABLE
| NODE_001 | ONLINE | 12ms | 99.9% |
| NODE_002 | IDLE | 8ms | 100% |
| NODE_003 | OFFLINE | — | 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
| NETD | 1042 | 0.1% | 12MB |
| ICE_CORE | 1078 | 2.3% | 84MB |
| UPLINK | 1091 | 0.8% | 32MB |
| SCAN_SVC | 1104 | 4.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_001 | NOMINAL | — |
| NODE_002 | SELECTED | PROCESSING |
| NODE_003 | CRITICAL | QUARANTINE |
<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 |