Stepper
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
<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.
<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
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
steps | steps | StepItem[] | [] | |
current | current | number | 0 | |
orientation | orientationreflects | 'horizontal' | 'vertical' | 'horizontal' | |
size | sizereflects | 'sm' | 'md' | 'lg' | 'md' | |
clickable | clickablereflects | boolean | false | |
iconOnly | icon-onlyreflects | boolean | false | |
labelBottom | label-bottomreflects | boolean | false | |
testId | string | Test ID for automated testing From ZenoElement. |
Methods
| Method | Description |
|---|---|
goToStep(index: number): void | Go to a specific step |
nextStep(): void | Go to next step |
previousStep(): void | Go to previous step |
setStepError(index: number, hasError: boolean): void | Mark step as error |
focus(): void | Focus the component or its first focusable element From ZenoElement. |
blur(): void | Blur the component or its focused element From ZenoElement. |
Events
| Event | Detail | Description |
|---|---|---|
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
| Key | What it does |
|---|---|
| Tab | With clickable, moves focus to each step that is not disabled, one after the other. Without clickable, no step can take focus. |
| EnterSpace | Makes the focused step the current step. |
| ArrowRightArrowDown | Makes the step after the focused step the current step. Focus stays where it is. |
| ArrowLeftArrowUp | Makes 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 thearia-labelof the element, or “Progress steps” when there is none. - Each step is a native
<button>whosearia-labelis the step label. The current step hasaria-current="step". - Without
clickable, every step button has the nativedisabledattribute. - The step label is rendered as an
<h3>and the description as a<p>next to the button. - With
prefers-reduced-motion: reducethe 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"andaria-selected, but there is notablistaround them and there are no tab panels. They sit directly insiderole="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"andaria-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-bottomattribute has no visible effect. The labels are below the buttons with or without it. goToStep()sendschangeeven 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.