Spec Label Section
A product spec section styled like a nutrition label — rows stamp themselves in one by one as it scrolls into the window, and any row can expand to show notes and supporting links.
npx shadcn@latest add https://webberui.com/r/spec-label-section.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<SpecLabelSection />
Installation
npx shadcn@latest add https://webberui.com/r/spec-label-section.jsonOr, once registries are configured in components.json, install it as @webberui/spec-label-section.
Usage
import {
SpecLabelSection,
type SpecItem,
} from "@/components/ui/spec-label-section";
const items: SpecItem[] = [
{
id: "battery",
label: "Battery life",
value: "38",
unit: "hours",
emphasis: true,
detail: "Measured in continuous playback with active noise cancelling on at 50% volume.",
links: [{ label: "Test method", href: "#" }],
},
{ id: "weight", label: "Weight", value: "248", unit: "g" },
];
<SpecLabelSection
title="Specs at a glance"
subtitle="Model NC-900 · per pair"
items={items}
/>;Props
SpecLabelSection
| Prop | Type | Default | Description |
|---|---|---|---|
items | SpecItem[] | — | Spec item data |
title | React.ReactNode | — | Section title, also used as the aria label |
subtitle | React.ReactNode | — | Subtitle below the title (model, serving size, and so on) |
featureHeading | React.ReactNode | "Item" | Column header: title of the feature column |
valueHeading | React.ReactNode | "Spec" | Column header: title of the value column |
footnote | React.ReactNode | — | Small footnote at the bottom (separated by a thick rule) |
openIds | string[] | — | Controlled mode: array of currently expanded row ids |
defaultOpenIds | string[] | [] | Initial array of expanded ids in uncontrolled mode |
onOpenChange | (ids: string[]) => void | — | Fires when the expanded state changes |
stagger | number | 0.08 | Interval between each row's stamp-in (seconds) |
delay | number | 0 | Delay before the reveal starts (seconds) |
duration | number | 0.3 | Expand / collapse duration (seconds) |
once | boolean | true | Play only the first time the element enters the viewport |
SpecItem
| Field | Type | Description |
|---|---|---|
id | string | Unique key, used to match controlled expansion — keep it stable |
label | React.ReactNode | Feature / item name (left side of the row) |
value | React.ReactNode | Main value (format it yourself) |
unit | React.ReactNode | Unit after the value (at a smaller size) |
emphasis | boolean | Emphasize this row with a thick rule and a larger number |
detail | React.ReactNode | Explanatory note shown once expanded |
links | SpecEvidenceLink[] | Supporting links shown once expanded |
SpecEvidenceLink
| Field | Type | Description |
|---|---|---|
label | React.ReactNode | Link text |
href | string | Link URL (opens in a new tab) |
How it works
- Stamp-in: when the whole table scrolls into the window, each row settles down from a slightly larger scale, accompanied by a fade in and a touch of blur, stamping in row by row according to
stagger. Setonce={false}to replay it every time it enters the viewport. - Thick and thin rules: the outer frame, the signature thick black bar, and the bottom edge of the header are thick rules; ordinary rows are separated by thin gray lines, while
emphasisrows switch to a thick black rule and enlarge the value, mimicking the hierarchy of a nutrition label. - Expandable rows: rows that carry
detailorlinksshow an indicator arrow and are clickable, expanding the note and supporting links below the row with a height transition; rows with neither render as static, non-interactive rows. - Controlled and uncontrolled: when
openIdsis not passed, the component manages the expanded state itself (usedefaultOpenIdsto set the initial one); passingopenIdsmakes it fully controlled, synchronized throughonOpenChange. Multiple rows may be expanded at once.
Accessibility
- Expandable rows are native
<button>s carryingaria-expandedandaria-controls; they are keyboard focusable and toggle with Enter / Space. - The expanded block is a
role="region"that points back at its trigger row througharia-labelledby. - When
titleis provided, the section is associated with its heading througharia-labelledby. - Supporting links open with
target="_blank"and carryrel="noopener noreferrer". - When the user has "reduce motion" enabled at the system level, the stamp-in and the expand transition are disabled and a static table is shown directly.
Blueprint Process Section
An engineering-blueprint take on the how-it-works layout — on graph paper, each step is a numbered, annotated station, and as you scroll the dashed leader lines, dimension callouts, and section marks are drawn in order as SVG stroke animations.
Milestone Unlock Meter
A full-bleed progress meter — the liquid level rises to fill the whole section from data or scroll, and every time it crosses a threshold the matching milestone card pops open, unlocked and lit.