Spec Ledger Section
A dense spec table with real header association, a focusable scroll region, and a sticky head that keeps its rule.
- Category
- Sections
- Added
- 2026-09-21
- Deps
- none
- Updated
- 2026-09-21
This is a viberdy Pro component
The live preview, the props panel and the write-up are open to everyone. The full source, the AI prompt and CLI install are part of Pro.
Specification
| Spec | Mark II | Mark III | Yard |
|---|---|---|---|
| Frame | Cold-rolled steel | Cold-rolled steel | Cast aluminium |
| Weight(g) | 340 | 382 | 1,240 |
| Depth(mm) | 18.5 | 18.5 | 42.0 |
| Finish | Bead blast | Bead blast | Anodised |
| Load(kg) | 6 | 9 | 40 |
| Warranty(yr) | 5 | 5 | 10 |
Props
"use client";
type Row = { spec: string; unit: string; values: string[] };
const COLS = ["Mark II", "Mark III", "Yard"];
const ROWS: Row[] = [
{ spec: "Frame", unit: "", values: ["Cold-rolled steel", "Cold-rolled steel", "Cast aluminium"] },
{ spec: "Weight", unit: "g", values: ["340", "382", "1,240"] },
{ spec: "Depth", unit: "mm", values: ["18.5", "18.5", "42.0"] },
{ spec: "Finish", unit: "", values: ["Bead blast", "Bead blast", "Anodised"] },
{ spec: "Load", unit: "kg", values: ["6", "9", "40"] },
{ spec: "Warranty", unit: "yr", values: ["5", "5", "10"] },
];
export function SpecLedgerSection({
title = "Specification",
note = "Measured at 20 °C. Tolerances ±2%.",
columns = COLS,
// ---------------------------------------------------------------
// Full source available with a viberdy Pro subscription.
// https://viberdy.dev/pricing
// ---------------------------------------------------------------
}About this section
A specification table is where a lot of otherwise careful design quietly stops being accessible, usually in three ways at once. The first is building it from divs: without real header cells and scope attributes a screen reader reads “340” with no way to know it is the weight of the Mark II, so the header association — not the hairlines or the alignment — is the actual information the table carries. The second is the scroll container. On a narrow viewport the table has to scroll sideways, and a scrolling box that nothing can focus is simply unreachable with a keyboard, because there is no gesture for “scroll that div”; a tabindex and a labelled region role make it a stop in the tab order and name it on arrival. The third is a detail that only shows up once the header is sticky: with collapsed borders the border belongs to the table rather than to the cell, so a sticky header scrolls away from its own rule and leaves a hairline floating in the body — separate borders with zero spacing look identical and behave. Sticky positioning also belongs on the header cells rather than on the row group, which is not reliably honoured, and those cells need an opaque background or the rows scroll visibly through them.
Curator’s note
A scrollable div that nothing can focus is unreachable by keyboard. tabindex=0 and a region label, every time.
Pairs well with
Matched on shared tags and category — the entries most likely to be used alongside this one.
Sortable Data Table
componentsA searchable table with three-state column sorting and a real loading skeleton.
Sticky Index Section
sectionsAn editorial index with sticky year headers, and a switch that demonstrates the overflow ancestor that breaks them.
Stacked Type Hero
sectionsOversized, tightly-stacked headline words that reveal in on mount — big type as sculpture.