docs / patterns / step-chips
StepChips
a short numbered sequence in one row, sized to sit inside a banner or under a heading rather than to carry a page.
intensity · structuresrsc · server
provenance
shaped from sisu-pluslayer
core / patternsintensity
structures · organizes a region, stays out of the contentcomposition
inside a CtaBanner or under a section heading, as a one-line pitchthree or four steps of a few words eachno interactive steps. it describes a sequence, it does not track oneno bodies under the steps, that is StepLista11y
an ordered list, so position comes free · the number and arrow arearia-hidden to avoid double announcing · takes aria-labeljest-axelive demo · try it out
- Install the extension
- Sign in
- Done
usage
import { StepChips } from "usva/patterns/step-chips";
<StepChips
aria-label="Setup steps"
steps={["Install the extension", "Sign in", "Done"]}
/>props
| prop | type | default | notes |
|---|---|---|---|
| steps | React.ReactNode[] | — | the steps, in order. keep each one a few words. |
| aria-label | string | — | names the sequence, e.g. "Setup steps". |
get it
npx shadcn add https://usva.build/r/step-chips.jsonsource · components/ui/step-chips.tsxexactly what this command copies
import * as React from "react";
import { cn } from "@/lib/utils";
export interface StepChipsProps
extends Omit<React.HTMLAttributes<HTMLOListElement>, "children"> {
steps: React.ReactNode[];
}
/**
* A numbered sequence of chips, joined by arrows. The arrow lives inside its step
* rather than between steps: an `ol` may only contain `li`, and sisu's sibling
* fragments put the arrows outside the list entirely.
*/
export const StepChips = React.forwardRef<HTMLOListElement, StepChipsProps>(
({ className, steps, ...props }, ref) => {
const last = steps.length - 1;
return (
<ol
ref={ref}
className={cn(
"flex list-none flex-wrap items-center gap-1.5",
className,
)}
{...props}
>
{steps.map((step, index) => (
<li
key={typeof step === "string" ? step : index}
className="inline-flex items-center gap-1.5"
>
<span className="inline-flex items-center gap-1.5 rounded-lg border border-border bg-ink/[0.04] px-2.5 py-1.5 text-xs text-muted">
<span
aria-hidden="true"
className="grid size-[1.1rem] shrink-0 place-items-center rounded-full bg-accent-alt/15 text-[0.62rem] font-bold tabular-nums text-accent-alt"
>
{index + 1}
</span>
{step}
</span>
{index < last && (
<span
data-step-separator=""
aria-hidden="true"
className="hidden text-muted sm:inline"
>
→
</span>
)}
</li>
))}
</ol>
);
},
);
StepChips.displayName = "StepChips";