Spec Ledger Section
A dense spec table with real header association, a focusable scroll region, and a sticky head that keeps its rule.
DOM + CSS0 deps1 file · 5 KB TSX
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
// ---------------------------------------------------------------
}Details
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.

