Skip to content

Steps

WxSteps is a sequence with a place in it: what is done, where you are, and what is left.

  1. 2DeliveryWhere it goes
  2. 3PaymentHow it is paid
  3. 4Done
Down the page
  1. UploadA CSV of up to 5 MB
  2. 2Map columnsMatch them to fields
  3. 3ReviewCheck a sample of rows

Usage

vue
<script setup lang="ts">
const current = ref(0)
</script>

<template>
  <wx-steps :current="current" aria-label="Checkout">
    <wx-step title="Details" description="Who it is for" />
    <wx-step title="Delivery" description="Where it goes" />
    <wx-step title="Payment" />
  </wx-steps>
</template>

It shows the place; it does not hold it. current is yours, and so is whatever is rendered underneath — a wizard that owned its own position would fight the router the first time somebody refreshed on step three.

Going back, never forward

clickable lets a finished step be pressed to return to it, and @change reports which. Steps ahead are never offered: they are the ones that have not been filled in, and offering them is a promise the form cannot keep.

Only finished steps render as buttons at all, so there is nothing for a keyboard to land on that would do nothing.

Something went wrong

error marks the current step as failed without moving off it — a payment refused, a validation the server rejected. The step keeps its place; only its colour and its glyph change.

Too narrow to be a row

Across the page a step is a marker, a title and a line of explanation side by side; on a phone there is room for about one of those. Rather than wrap every title to a letter a line, a horizontal sequence turns down the page on its own once a step would be narrower than minStepWidth — 132 pixels by default. :min-step-width="0" keeps it across the page whatever the room.

How many steps there are is half the question, so this is a measurement of the sequence's own box rather than a media query: four steps need twice the room two do, and it is the panel a wizard sits in that decides, not the window.

Numbering

The steps report themselves in the order they are written, while the component sets up rather than after it mounts. That matters: a sequence that numbered itself one frame later would flicker on every page carrying a wizard.

The rule between them is a pseudo-element chosen by :last-child, for the same reason — a step that had to be told how many siblings it has cannot know on its first render.

Steps

PropTypeDefaultDescription
currentnumber0Which step, counting from zero
direction'horizontal' | 'vertical''horizontal'Across, or down the page
size'sm' | 'md''md'Marker and title size
errorbooleanfalseThe current step went wrong
clickablebooleanfalseFinished steps can be returned to
minStepWidthnumber132Narrower than this and it folds down the page; 0 never
ariaLabelstringAccessible name of the sequence

Events: change (index) — a finished step was chosen.

Step

PropTypeDefaultDescription
titlestringWhat this step is
descriptionstringWhat it involves
iconIconNameInstead of the number

Slots: title; default — the description; marker — with { index, state }.

Accessibility

The sequence is an ordered list, which is what it is, and the current step carries aria-current="step". The state is never colour alone: a finished step shows a tick, a failed one a cross, and the ones ahead their number.

Released under the MIT License.