Step Widgets
A thin shell for stepwise interactive lessons. The rail at the top names each step; the body below renders the active step's widget — a slider, a diagram, a markup walkthrough, anything. Controlled or uncontrolled; full tablist semantics; keyboard navigation built in.
Preview
AVIF ships roughly half the bytes of JPEG at matched quality. WebP is the universally-supported fallback. JPEG remains the safety net for the last <img> in a <picture>.
Customize
Installation
npx shadcn@latest add https://craftbits.dev/r/step-widgets.jsonUsage
import { StepWidgets } from "@craft-bits/core";
<StepWidgets
steps={[
{ id: "format", label: "Format", content: <FormatLandscape /> },
{ id: "quality", label: "Quality", content: <CompressionQuality /> },
{ id: "srcset", label: "Srcset", content: <SrcsetBuilder /> },
]}
/>Controlled mode — drive activeStep from outside:
const [step, setStep] = useState("format");
<StepWidgets
activeStep={step}
onActiveStepChange={(id) => setStep(id)}
steps={[
{ id: "format", label: "Format", content: <FormatLandscape /> },
{ id: "quality", label: "Quality", content: <CompressionQuality /> },
]}
/>Anatomy
- Step rail — a
role="tablist"row (or column) of buttons. Each shows a numbered accent pill, the step'slabel, and an optionaldescriptionslot. The active trigger picks up an accent-muted fill and a 2px accent underline. - Step body — a single
role="tabpanel". Either passcontentper step or passchildrento the whole component for a static panel. - Orientation —
horizontal(default, rail on top) orvertical(rail on the left atmd+).
Props
| Prop | Type | Default | Description |
|---|---|---|---|
steps | StepWidgetsStep[] | required | Ordered step descriptors. |
activeStep | string | — | Controlled active step id. |
defaultActiveStep | string | first step id | Initial step in uncontrolled mode. |
onActiveStepChange | (id, index) => void | — | Fired on tab change. |
orientation | 'horizontal' | 'vertical' | 'horizontal' | Rail layout. |
renderTrigger | (args) => ReactNode | — | Override the trigger element. |
children | ReactNode | — | Static body, replaces the per-step content. |
className | string | — | Merged onto the root via cn(). |
StepWidgetsStep
| Field | Type | Description |
|---|---|---|
id | string | Stable identifier. |
label | ReactNode | Visible name on the rail. |
description | ReactNode | Optional sub-label under the name. |
content | ReactNode | (index) => ReactNode | Body for this step. |
Accessibility
- The rail is a
role="tablist"witharia-orientationset to match the layout. - Each trigger is a
role="tab"witharia-selected,aria-controls, and rovingtabindex. - The body is a
role="tabpanel"linked byaria-labelledbyto the active trigger. - Arrow keys move between triggers; Home and End jump to the first or last step.
Credits
- Extracted from:
terminal-dreams(src/components/frontend-design/perf-images/ui/StepWidgets.tsx). The original was a bundle of six lesson-specific widgets; this primitive keeps the stepwise shell and pushes every domain-specific concern back to the consumer's panel content.