Stepper
Stepper shows progress through a short, ordered flow and derives every step status from one active index.
import { Stepper } from '@aksornproject/aksara-ui'Basic stepper
steps is an array of title and optional description, in flow order. activeStep is the 0-based index of the current step. Earlier steps are complete and later ones are incomplete. Each step is a Step in a list item.
- Your detailsCompletedName and email
- AddressWhere to send it
- PaymentNot startedChoose a method
<Stepper
aria-label="Enrolment progress"
steps={steps}
activeStep={1}
className="w-full"
/>Moving through the flow
Stepper holds no state. Keep the index in your own state and pass it as activeStep. An index past the last step makes every step complete.
- Your detailsName and email
- AddressNot startedWhere to send it
- PaymentNot startedChoose a method
<div className="flex w-full flex-col gap-spacing-16">
<Stepper
aria-label="Enrolment progress"
steps={steps}
activeStep={active}
/>
<div className="flex gap-spacing-8">
<Button
type="button"
variant="secondary-brand"
disabled={active === 0}
onClick={() => setActive((step) => Math.max(step - 1, 0))}
>
Back
</Button>
<Button
type="button"
disabled={active === steps.length}
onClick={() => setActive((step) => Math.min(step + 1, steps.length))}
>
Continue
</Button>
</div>
</div>Not started and all complete
A negative activeStep means nothing has started. activeStep="complete" marks every step complete. Fractions truncate and NaN counts as 0, so every number is a valid input.
- Your detailsNot startedName and email
- AddressNot startedWhere to send it
- PaymentNot startedChoose a method
- Your detailsCompletedName and email
- AddressCompletedWhere to send it
- PaymentCompletedChoose a method
<div className="flex w-full flex-col gap-spacing-24">
<Stepper aria-label="Not started" steps={steps} activeStep={-1} />
<Stepper aria-label="All complete" steps={steps} activeStep="complete" />
</div>Orientation and size
orientation="vertical" stacks the steps with an 8px gap. size sets sm, md or lg on every step. The defaults are horizontal and sm. Stepper never switches orientation by itself, so pick vertical for narrow screens.
- Your detailsCompletedName and email
- AddressCompletedWhere to send it
- PaymentChoose a method
- ReviewNot startedCheck and confirm
<Stepper
aria-label="Enrolment progress"
steps={steps}
activeStep={2}
orientation="vertical"
size="md"
className="w-[320px]"
/>Narrow containers
Horizontal steps share the width equally, with a 160px minimum each and a 16px gap. They never wrap. Below that width the list overflows, so the page owns the scrolling.
- Your detailsCompletedName and email
- AddressWhere to send it
- PaymentNot startedChoose a method
- ReviewNot startedCheck and confirm
<div className="w-[326px] overflow-x-auto">
<Stepper aria-label="Enrolment progress" steps={steps} activeStep={1} />
</div>Custom styles
indicator, titleClassName and descriptionClassName apply to every step. Each takes one value for every status or a map keyed by status. Per-step differences need a hand-written list of Step parts.
- Section ACompletedMultiple choice
- Section BShort answers
- Section CNot startedEssay
<Stepper
aria-label="Exam sections"
steps={steps}
activeStep={1}
titleClassName={{ complete: 'text-color-text-secondary' }}
descriptionClassName={typography('supporting-regular')}
className="w-full"
/>Accessibility
- The root is an
olwithrole="list", which keeps list semantics in Safari. Give it anaria-labelwhen the page has more than one flow. - Only the current step's list item has
aria-current="step". - Complete and incomplete steps add visually hidden words after the title, "Completed" and "Not started" by default. Localise them with
statusLabels. - Indicators and segments are decorative and
aria-hidden. - Steps are not tabs, buttons or links. Put navigation controls outside the list and keep titles and descriptions noninteractive.
API
Stepper
Prop
Type
StepperItem
Prop
Type