Stepper

A vertical, numbered list of steps with an optional active step that can follow the scroll position.

Stepper lays out a short sequence as a vertical, numbered list: a number in a circle for each step, a 1px line joining it to the next, and the title and description to the right. activeStep highlights one step in Base blue. It is a compound component: the stepper provides the list, and Stepper.Step, Stepper.Title and Stepper.Description place the content. Steps number themselves in the order they appear. The slots are also named exports (StepperStep, StepperTitle, StepperDescription); use those in a React Server Component, where the Stepper.Step properties of the client module are not available.

import { Stepper } from '@base-org/bds-web/product/stepper';

<Stepper activeStep={0}>
  <Stepper.Step>
    <Stepper.Title>Deposit</Stepper.Title>
    <Stepper.Description>A customer deposits supported stablecoins in your app.</Stepper.Description>
  </Stepper.Step>
  <Stepper.Step>
    <Stepper.Title>Earn</Stepper.Title>
    <Stepper.Description>Deposits earn a variable rate from borrowers across lending markets on Base.</Stepper.Description>
  </Stepper.Step>
</Stepper>;

Variants

Steppers are vertical only for now.

Medium

The default, size="md", on the Product scale: a 24px number in label-medium, titles in title-3, descriptions in body, and 24px between steps.

  1. Deposit

    A customer deposits supported stablecoins in your app.

  2. Earn

    Deposits earn a variable rate from borrowers across lending markets on Base.

  3. Withdraw

    Customers can withdraw and stop earning, subject to available liquidity and protocol terms.

<Stepper activeStep={0}>
<Stepper.Step>
  <Stepper.Title>Deposit</Stepper.Title>
  <Stepper.Description>A customer deposits supported stablecoins in your app.</Stepper.Description>
</Stepper.Step>
<Stepper.Step>
  <Stepper.Title>Earn</Stepper.Title>
  <Stepper.Description>Deposits earn a variable rate from borrowers across lending markets on Base.</Stepper.Description>
</Stepper.Step>
<Stepper.Step>
  <Stepper.Title>Withdraw</Stepper.Title>
  <Stepper.Description>Customers can withdraw and stop earning, subject to available liquidity and protocol terms.</Stepper.Description>
</Stepper.Step>
</Stepper>

Large

size="lg" is the larger marketing scale: a 32px number, titles on the brand title-3 step, descriptions on the brand body step up to 520px wide, and 40px between steps. Use it for a sequence in a marketing section, such as the How it works list on base.org.

  1. Deposit

    A customer deposits supported stablecoins in your app.

  2. Earn

    Deposits earn a variable rate from borrowers across lending markets on Base.

  3. Withdraw

    Customers can withdraw and stop earning, subject to available liquidity and protocol terms.

<Stepper size="lg" activeStep={1}>
<Stepper.Step>
  <Stepper.Title>Deposit</Stepper.Title>
  <Stepper.Description>A customer deposits supported stablecoins in your app.</Stepper.Description>
</Stepper.Step>
<Stepper.Step>
  <Stepper.Title>Earn</Stepper.Title>
  <Stepper.Description>Deposits earn a variable rate from borrowers across lending markets on Base.</Stepper.Description>
</Stepper.Step>
<Stepper.Step>
  <Stepper.Title>Withdraw</Stepper.Title>
  <Stepper.Description>Customers can withdraw and stop earning, subject to available liquidity and protocol terms.</Stepper.Description>
</Stepper.Step>
</Stepper>
SizeUse for
mdThe default: a sequence on a product surface, such as setup steps.
lgA sequence in a marketing section, with a 32px number and brand type steps.

No active step

Leave out activeStep and every step stays gray: a plain numbered list with nothing highlighted.

  1. Create a key

  2. Add it to your app

<Stepper>
<Stepper.Step>
  <Stepper.Title>Create a key</Stepper.Title>
</Stepper.Step>
<Stepper.Step>
  <Stepper.Title>Add it to your app</Stepper.Title>
</Stepper.Step>
</Stepper>

Scroll tracking

useStepperScroll(ref, count) returns the index of the step whose vertical span the centre of the viewport is passing through. Give it the stepper's ref and the number of steps, and pass the result to activeStep. The highlight moves forwards and backwards as the reader scrolls, and the range scales with the list's own height rather than the viewport's. Scroll the page past this example to see it.

  1. Choose an amount

    A customer picks how much to borrow against the crypto they already hold in your app.

  2. Get the loan instantly

    They receive USDC straight away. Their collateral moves onchain and backs the loan.

  3. Repay anytime

    No schedule and no deadlines. Your customers repay when they want and get their collateral back.

'use client';

import { useRef } from 'react';
import { Stepper, useStepperScroll } from '@base-org/bds-web/product/stepper';

function HowItWorksSteps({ steps }) {
const ref = useRef<HTMLOListElement>(null);
const activeStep = useStepperScroll(ref, steps.length);

return (
  <Stepper ref={ref} size="lg" activeStep={activeStep}>
    {steps.map((step) => (
      <Stepper.Step key={step.title}>
        <Stepper.Title>{step.title}</Stepper.Title>
        <Stepper.Description>{step.description}</Stepper.Description>
      </Stepper.Step>
    ))}
  </Stepper>
);
}

The number's colour fades over 500ms as the active step changes, a slow change that follows scrolling rather than a direct response to input. Under reduced motion it changes at once.

When to use

Use Stepper to show a short, ordered sequence a reader follows in order, such as the steps of a flow or a How it works list.

Use something else when:

  • The items have no order. Use a Card Group of Cards, or a plain list.
  • Each step is a tile in a grid. Use a numbered Card with Card.Number.
  • The reader moves between views of the same content. Use Tabs or Segment.

The base.org How it works section, with its heading, tabs and media, stays a copyable pattern built on Stepper rather than a package component.

Usage

Do

Keep a stepper to a handful of steps, each with a short title and one sentence of description.

Don't

Use the active step to show progress through a form the reader controls; Stepper does not make steps interactive or track completion.

  • Put each step's content in Stepper.Title and an optional Stepper.Description. The number and line come from the step.
  • Make each Stepper.Step a direct child of the stepper, so it is numbered.
  • activeStep is 0-based. Leave it out for no highlight.
  • Call useStepperScroll in a client component, with the same ref the stepper gets.

Props

Stepper

PropTypeDefault
activeStep

The 0-based index of the step to highlight. Leave it out for no

TypenumberDefault—
size

`md` is the Product scale: a 24px number and `title-3` titles. `lg` is

Type"md" | "lg"Defaultmd

Stepper.Step takes the props of an li, Stepper.Title those of an h3, and Stepper.Description those of a p; none adds props of its own. Each slot reads the stepper's size, and the step's index and active state.

useStepperScroll

ArgumentTypeDescription
refRefObject<HTMLElement | null>The ref passed to the stepper.
countnumberThe number of steps.

It returns the 0-based index of the active step, starting at 0.

Accessibility

Stepper renders an ordered list and each step a list item, so assistive technology announces how many steps there are and where each one sits. The number in its circle and the connecting line are decorative and hidden; the list carries the order.

The active step has aria-current="step". With no activeStep, no step is marked current.

Stepper.Title renders an h3, so the steps sit under the section's h2 heading.

There is no React Aria primitive for a stepper. Stepper is not interactive: it has no focus stop and no keyboard behaviour of its own, and links or buttons inside a step keep theirs.