Skip to main content

Stepper

<zeno-stepper>NavigationSource

The steps of a process and how far along it is.

import '@usezeno/tokens/css';
import '@usezeno/core/web-components';

Examples​

Basic​

Steps are set with the steps property. current is the index of the current step and starts at 0. Steps before it show a check mark.

<zeno-stepper current="1"></zeno-stepper>
document.querySelector('zeno-stepper').steps = [
{ value: 'account', label: 'Account', description: 'Email and password' },
{ value: 'profile', label: 'Profile', description: 'Name and time zone' },
{ value: 'review', label: 'Review', description: 'Check your details' },
];

Clickable steps​

With clickable, a click on a step makes it the current step. The nextStep() and previousStep() methods do the same from your own buttons. Each change sends a change event.

Step 1: Cart

Back Next
<zeno-stepper clickable></zeno-stepper>
<p data-result>Step 1: Cart</p>
<zeno-button variant="outline">Back</zeno-button>
<zeno-button>Next</zeno-button>
const stepper = document.querySelector('zeno-stepper');
stepper.steps = [
{ value: 'cart', label: 'Cart' },
{ value: 'address', label: 'Address' },
{ value: 'payment', label: 'Payment' },
{ value: 'done', label: 'Done' },
];
stepper.addEventListener('change', event => {
document.querySelector('[data-result]').textContent =
'Step ' + (event.detail.current + 1) + ': ' + event.detail.step.label;
});
const buttons = document.querySelectorAll('zeno-button');
buttons[0].addEventListener('click', () => stepper.previousStep());
buttons[1].addEventListener('click', () => stepper.nextStep());

Vertical​

<zeno-stepper orientation="vertical" current="1"></zeno-stepper>
document.querySelector('zeno-stepper').steps = [
{ value: 'upload', label: 'Upload the file', description: 'report.csv' },
{ value: 'check', label: 'Check the columns', description: 'Match each column to a field' },
{ value: 'import', label: 'Import', description: 'Add the rows to your list' },
];

Sizes, icons and icon only​

A step can name an icon in its icon field. icon-only hides the titles and descriptions.

<zeno-stepper size="sm" current="1"></zeno-stepper>
<zeno-stepper size="lg" current="1"></zeno-stepper>
<zeno-stepper icon-only current="1"></zeno-stepper>
document.querySelectorAll('zeno-stepper').forEach(stepper => {
stepper.steps = [
{ value: 'account', label: 'Account', icon: 'user' },
{ value: 'settings', label: 'Settings', icon: 'settings' },
{ value: 'finish', label: 'Finish', icon: 'star' },
];
});

Error and disabled steps​

setStepError(index, true) marks a step with an error. A step with disabled: true cannot become the current step.

Mark step 2 as failed Clear the error
<zeno-stepper clickable current="1"></zeno-stepper>
<zeno-button variant="outline">Mark step 2 as failed</zeno-button>
<zeno-button variant="ghost">Clear the error</zeno-button>
const stepper = document.querySelector('zeno-stepper');
stepper.steps = [
{ value: 'details', label: 'Details' },
{ value: 'payment', label: 'Payment' },
{ value: 'invoice', label: 'Invoice', disabled: true },
];
const buttons = document.querySelectorAll('zeno-button');
buttons[0].addEventListener('click', () => stepper.setStepError(1, true));
buttons[1].addEventListener('click', () => stepper.setStepError(1, false));

Attributes and properties​

PropertyAttributeTypeDefaultDescription
stepsstepsStepItem[][]
currentcurrentnumber0
orientationorientationreflects'horizontal' | 'vertical''horizontal'
sizesizereflects'sm' | 'md' | 'lg''md'
clickableclickablereflectsbooleanfalse
iconOnlyicon-onlyreflectsbooleanfalse
labelBottomlabel-bottomreflectsbooleanfalse
testIdstringTest ID for automated testing From ZenoElement.

Methods​

MethodDescription
goToStep(index: number): voidGo to a specific step
nextStep(): voidGo to next step
previousStep(): voidGo to previous step
setStepError(index: number, hasError: boolean): voidMark step as error
focus(): voidFocus the component or its first focusable element From ZenoElement.
blur(): voidBlur the component or its focused element From ZenoElement.

Events​

EventDetailDescription
change{ current: number; step: StepItem }The current step changed.

Slots​

This element has no slots.

CSS parts​

Part
base
content
description
icon
indicator
item
line
step
title

CSS custom properties​

This element documents no custom properties of its own. Style it through its parts and through the tokens it reads.

The element's own source reads no tokens.

Types​

interface StepItem {
value: string;
label: string;
description?: string;
icon?: string;
disabled?: boolean;
}

Accessibility​

Keyboard​

KeyWhat it does
TabWith clickable, moves focus to each step that is not disabled, one after the other. Without clickable, no step can take focus.
EnterSpaceMakes the focused step the current step.
ArrowRightArrowDownMakes the step after the focused step the current step. Focus stays where it is.
ArrowLeftArrowUpMakes the step before the focused step the current step. Focus stays where it is.

Forms​

This element is not a form control. It adds nothing to a form's data.

Notes​

  • The steps are inside a <div role="navigation">. Its name is the aria-label of the element, or “Progress steps” when there is none.
  • Each step is a native <button> whose aria-label is the step label. The current step has aria-current="step".
  • Without clickable, every step button has the native disabled attribute.
  • The step label is rendered as an <h3> and the description as a <p> next to the button.
  • With prefers-reduced-motion: reduce the transitions are turned off.

Tests and known gaps​

  • No tests of its own. The only check is that the element registers and renders (smoke.test.ts).
  • Not checked with axe-core.
  • Has no Storybook story.
16 of the 24 properties, methods, events, slots and parts above have no description in the source
  • the property steps
  • the property current
  • the property orientation
  • the property size
  • the property clickable
  • the property iconOnly
  • the property labelBottom
  • the part base
  • the part content
  • the part description
  • the part icon
  • the part indicator
  • the part item
  • the part line
  • the part step
  • the part title

Known gaps:

  • Step buttons have role="tab" and aria-selected, but there is no tablist around them and there are no tab panels. They sit directly inside role="navigation".
  • Without clickable, a stepper that only shows progress is a group of disabled buttons.
  • The completed and error states are not given to assistive technology. Each button is named by the step label only, and the check and x icons have no text.
  • When the current step is marked with an error, it loses aria-current="step" and aria-selected="true".
  • The arrow keys change the current step but do not move focus, so a second press of the same key does nothing new. Every enabled step is its own tab stop.
  • The label-bottom attribute has no visible effect. The labels are below the buttons with or without it.
  • goToStep() sends change even when the step is already the current step.
  • nextStep(), previousStep() and the arrow keys stop at a disabled step. They do not skip over it.
  • Step labels are always <h3> elements. The heading level cannot be changed to fit the page.