Klarhimmel UI

Stepper

Stable

Where someone is in a flow: numbered steps, the current one marked, each step's state said in words.

npx shadcn add @klarhimmel/stepper

When to use

  • Use for

    Showing progress through checkout, booking or onboarding.

  • Not for

    A percentage. Use Progress. Switching between views. Use Tabs.

  • Pairs with

    Button, Progress, a heading per step.

Preview

  1. Dates, Completed
  2. Guests, Current step
  3. Payment, Upcoming
  4. Done, Upcoming
stepper.tsx
import { Theme } from '@/components/klarhimmel/theme/style';
import { Stepper } from '@/components/klarhimmel/ui/stepper';

export default function Example() {
  return (
    <Theme theme="brand">
      <Stepper
        aria-label="Booking progress"
        current={1}
        steps={[{ label: 'Dates' }, { label: 'Guests' }, { label: 'Payment' }]}
      />
    </Theme>
  );
}

Install

Import

import { Stepper } from '@/components/klarhimmel/ui/stepper';
Lands in
components/klarhimmel/ui/stepper.tsx, plus cn() and the theme item if they are missing. Import theme.css after Tailwind.
Render in
Render it inside <Theme>, once near the root of your app. Tokens and fonts are scoped to it, so outside it the component is unstyled.

Examples

  • Descriptions

    1. Pay, Current stepCard or invoice

    You are on step 3: Pay. Completed steps are buttons.

    import { useState } from 'react';
    import { Button } from '@/components/klarhimmel/ui/button';
    import { Stepper } from '@/components/klarhimmel/ui/stepper';
    
    const steps = [
      { label: 'Choose a room', description: 'Seaside, garden or loft' },
      { label: 'Your details', description: 'Name and email' },
      { label: 'Pay', description: 'Card or invoice' },
    ];
    
    export default function StepperDescriptions() {
      const [current, setCurrent] = useState(2);
      return (
        <div className="flex w-[min(44rem,calc(100cqw-3rem))] flex-col gap-6">
          <Stepper aria-label="Checkout" steps={steps} current={current} onStepSelect={setCurrent} />
          <p role="status" className="text-kh-small text-kh-muted">You are on step {current + 1}: {steps[current]!.label}. Completed steps are buttons.</p>
          <div><Button variant="secondary" onClick={() => setCurrent(2)}>Jump to pay</Button></div>
        </div>
      );
    }
  • Narrow

    1. Create account, Done
    2. Add your hotel, You are hereName and address
    3. Invite the team, Next

    In a narrow container the steps run down the side.

    import { Stepper } from '@/components/klarhimmel/ui/stepper';
    
    export default function StepperNarrow() {
      return (
        <div className="w-64 rounded-kh-lg border border-kh-line p-4">
          <Stepper
            aria-label="Onboarding"
            current={1}
            steps={[{ label: 'Create account' }, { label: 'Add your hotel', description: 'Name and address' }, { label: 'Invite the team' }]}
            labels={{ complete: 'Done', current: 'You are here', upcoming: 'Next' }}
          />
          <p className="mt-4 text-kh-caption text-kh-muted">In a narrow container the steps run down the side.</p>
        </div>
      );
    }

API reference

PropTypeDefaultDescription
steps{ label: string; description?: string }[]-Required, in order.
currentnumber-Required. The step the person is on, counting from 0. Earlier steps are complete.
aria-labelstring-Required: names the list.
labels{ complete; current; upcoming }English words'Completed', 'Current step' and 'Upcoming' by default: what assistive tech hears beside each step. Pass them translated.
onStepSelect(index: number) => void-With it, completed steps are buttons that go back.
classNamestring-Extra classes, merged after the component's own.

Accessibility

KeyDoes
TabReaches completed steps when onStepSelect is given
Enter / SpaceGoes back to the focused completed step

An ordered list; the current step is aria-current="step" and every step says its state in words, so state is never colour alone.

Accessibility