md-stepper spec card
Identity · when to use / when NOT · decision cues · behavioural contract · do/don't · anti-patterns · full API. Paste into your agent when you're implementing with this component.
A linear, ordered process broken into steps. It orchestrates slotted
md-step children: it owns the active index, the
navigation buttons, and whether the user may jump ahead.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-stepper id="st-checkout" label="Checkout progress" style="inline-size: 100%;">
<md-step label="Cart"></md-step>
<md-step label="Shipping" description="Address and method"></md-step>
<md-step label="Payment"></md-step>
<md-step label="Review"></md-step>
<div slot="content">
<div data-panel="0" style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Your cart</div>
<div>3 items · $128.40 — step forward to choose delivery.</div>
</div>
<div data-panel="1" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Shipping</div>
<div>Standard delivery, 3–5 days. Free over $50.</div>
</div>
<div data-panel="2" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Payment</div>
<div>Card ending 4242. Billing address matches shipping.</div>
</div>
<div data-panel="3" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Review</div>
<div>Everything looks right? Finishing places the order.</div>
</div>
</div>
</md-stepper>
<script type="module">
var stepper = document.getElementById('st-checkout');
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-checkout [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
</script>import { useEffect, useRef } from 'react';
import { MdStep, MdStepper } from '@awc-ui/react';
export function Demo() {
const stepperRef = useRef(null);
useEffect(() => {
const stepper = stepperRef.current;
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-checkout [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
}, []);
return (
<>
<MdStepper id="st-checkout" ref={stepperRef} label="Checkout progress" style={{ inlineSize: '100%' }}>
<MdStep label="Cart"></MdStep>
<MdStep label="Shipping" description="Address and method"></MdStep>
<MdStep label="Payment"></MdStep>
<MdStep label="Review"></MdStep>
<div slot="content">
<div data-panel="0" style={{ padding: '4px 0 8px', font: 'var(--md-sys-typescale-body-medium)', color: 'var(--md-sys-color-on-surface-variant)' }}>
<div style={{ font: 'var(--md-sys-typescale-title-small)', color: 'var(--md-sys-color-on-surface)' }}>Your cart</div>
<div>3 items · $128.40 — step forward to choose delivery.</div>
</div>
<div data-panel="1" hidden style={{ padding: '4px 0 8px', font: 'var(--md-sys-typescale-body-medium)', color: 'var(--md-sys-color-on-surface-variant)' }}>
<div style={{ font: 'var(--md-sys-typescale-title-small)', color: 'var(--md-sys-color-on-surface)' }}>Shipping</div>
<div>Standard delivery, 3–5 days. Free over $50.</div>
</div>
<div data-panel="2" hidden style={{ padding: '4px 0 8px', font: 'var(--md-sys-typescale-body-medium)', color: 'var(--md-sys-color-on-surface-variant)' }}>
<div style={{ font: 'var(--md-sys-typescale-title-small)', color: 'var(--md-sys-color-on-surface)' }}>Payment</div>
<div>Card ending 4242. Billing address matches shipping.</div>
</div>
<div data-panel="3" hidden style={{ padding: '4px 0 8px', font: 'var(--md-sys-typescale-body-medium)', color: 'var(--md-sys-color-on-surface-variant)' }}>
<div style={{ font: 'var(--md-sys-typescale-title-small)', color: 'var(--md-sys-color-on-surface)' }}>Review</div>
<div>Everything looks right? Finishing places the order.</div>
</div>
</div>
</MdStepper>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
// app.component.ts
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';
@Component({
selector: 'app-demo',
templateUrl: './app.component.html',
})
export class DemoComponent implements AfterViewInit {
@ViewChild('stepper') stepperRef!: ElementRef;
ngAfterViewInit() {
const stepper = this.stepperRef.nativeElement;
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-checkout [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
}
}
<!-- app.component.html -->
<md-stepper id="st-checkout" #stepper label="Checkout progress" style="inline-size: 100%;">
<md-step label="Cart"></md-step>
<md-step label="Shipping" description="Address and method"></md-step>
<md-step label="Payment"></md-step>
<md-step label="Review"></md-step>
<div slot="content">
<div data-panel="0" style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Your cart</div>
<div>3 items · $128.40 — step forward to choose delivery.</div>
</div>
<div data-panel="1" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Shipping</div>
<div>Standard delivery, 3–5 days. Free over $50.</div>
</div>
<div data-panel="2" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Payment</div>
<div>Card ending 4242. Billing address matches shipping.</div>
</div>
<div data-panel="3" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Review</div>
<div>Everything looks right? Finishing places the order.</div>
</div>
</div>
</md-stepper><script setup>
import { onMounted, ref } from 'vue';
import '@awc-ui/core/define';
const stepperRef = ref(null);
onMounted(() => {
const stepper = stepperRef.value;
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-checkout [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
});
</script>
<template>
<md-stepper id="st-checkout" ref="stepperRef" label="Checkout progress" style="inline-size: 100%;">
<md-step label="Cart"></md-step>
<md-step label="Shipping" description="Address and method"></md-step>
<md-step label="Payment"></md-step>
<md-step label="Review"></md-step>
<div slot="content">
<div data-panel="0" style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Your cart</div>
<div>3 items · $128.40 — step forward to choose delivery.</div>
</div>
<div data-panel="1" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Shipping</div>
<div>Standard delivery, 3–5 days. Free over $50.</div>
</div>
<div data-panel="2" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Payment</div>
<div>Card ending 4242. Billing address matches shipping.</div>
</div>
<div data-panel="3" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Review</div>
<div>Everything looks right? Finishing places the order.</div>
</div>
</div>
</md-stepper>
</template><script>
import { onMount } from 'svelte';
import '@awc-ui/core/define';
let stepperRef;
onMount(() => {
const stepper = stepperRef;
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-checkout [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
});
</script>
<md-stepper id="st-checkout" bind:this={stepperRef} label="Checkout progress" style="inline-size: 100%;">
<md-step label="Cart"></md-step>
<md-step label="Shipping" description="Address and method"></md-step>
<md-step label="Payment"></md-step>
<md-step label="Review"></md-step>
<div slot="content">
<div data-panel="0" style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Your cart</div>
<div>3 items · $128.40 — step forward to choose delivery.</div>
</div>
<div data-panel="1" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Shipping</div>
<div>Standard delivery, 3–5 days. Free over $50.</div>
</div>
<div data-panel="2" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Payment</div>
<div>Card ending 4242. Billing address matches shipping.</div>
</div>
<div data-panel="3" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Review</div>
<div>Everything looks right? Finishing places the order.</div>
</div>
</div>
</md-stepper>
Already installed? See the
Installation guide for one-time package setup
(core + tokens, fonts). Each tab below shows two patterns for using
md-stepper in your project: Option A registers every AWC UI
component at once (simplest), Option B imports only this component for
tree-shake-friendly bundles.
<!-- ─── Option A: global registration (all components) ─── -->
<script type="module">
import '@awc-ui/core/define';
</script>
<!-- ─── Option B: single import (tree-shake only md-stepper) ─── -->
<script type="module">
import '@awc-ui/core/components/md-stepper';
</script>
<md-stepper></md-stepper>// ─── Option A: typed React wrapper (registers all components) ───
// Importing from '@awc-ui/react' calls defineCustomElements() as a
// side effect, so every md-* element becomes available in the browser.
import { MdStepper } from '@awc-ui/react';
export function Example() {
return <MdStepper></MdStepper>;
}
// ─── Option B: single import (tree-shake to only md-stepper) ───
// Skip the wrapper and use the raw custom element. Smallest bundle,
// but you lose typed props/events on JSX.
import '@awc-ui/core/components/md-stepper';
export function ExampleTreeShaken() {
return <md-stepper></md-stepper>;
}// ─── Option A: schema module (any md-* element accepted) ───
// Pair with `defineCustomElements(window)` in main.ts.
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@Component({
standalone: true,
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `<md-stepper></md-stepper>`,
})
export class ExampleComponent {}
// ─── Option B: typed directive (tree-shake friendly) ───
// Pair with `import '@awc-ui/core/components/md-stepper'` in main.ts.
import { Component } from '@angular/core';
import { MdStepper } from '@awc-ui/angular';
@Component({
standalone: true,
imports: [MdStepper],
template: `<md-stepper></md-stepper>`,
})
export class ExampleTreeShakenComponent {}<!-- ─── Option A: typed Vue wrapper (registers all components) ─── -->
<script setup lang="ts">
import { MdStepper } from '@awc-ui/vue';
</script>
<template>
<MdStepper></MdStepper>
</template>
<!-- ─── Option B: single import (tree-shake to only md-stepper) ─── -->
<script setup lang="ts">
import '@awc-ui/core/components/md-stepper';
</script>
<template>
<md-stepper></md-stepper>
</template><!-- ─── Option A: global registration (done once in main entry) ─── -->
<!-- main.ts: -->
<!-- import { defineCustomElements } from '@awc-ui/svelte'; -->
<!-- defineCustomElements(window); -->
<md-stepper></md-stepper>
<!-- ─── Option B: single import (tree-shake to only md-stepper) ─── -->
<script lang="ts">
import '@awc-ui/core/components/md-stepper';
</script>
<md-stepper></md-stepper>mdBeforeChange).| Situation | Use instead |
|---|---|
| Peer views the user switches between freely | md-tabs |
| Independent collapsible sections | md-accordion |
| Top-level destinations | md-navigation-bar / md-navigation-rail |
| Simple progress reporting | md-progress-indicator |
| A two-field form | Just show the form |
| Hierarchy rather than sequence | Breadcrumbs |
| Need | Setting |
|---|---|
| Steps must be done in order | mode="linear" (default) |
| Any step reachable | mode="non-linear" and editable on the steps |
| Panel per step, expanding in place | orientation="vertical" |
| One shared panel you swap | orientation="horizontal" + the content slot |
| Minimal dots instead of numbers | indicator="dot" |
| Compact bar for phones | variant="mobile" |
| Your own buttons | nav="false" |
| Block a transition | Cancel mdBeforeChange |
mdBeforeChangeThe demo below runs exactly the handler in its code panel. Continue advances normally from Cart and Shipping; on Payment the gate cancels the move and paints the reason on the step. Back is never gated:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-stepper id="wiz" label="Checkout">
<md-step label="Cart"></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<md-step label="Review"></md-step>
<div slot="content">
<span id="msg">Press Continue. Cart and Shipping advance normally — Payment is the one that fails.</span>
<md-button id="fix" variant="tonal" size="sm" hidden>Use a valid card</md-button>
</div>
</md-stepper>
<script type="module">
const wiz = document.getElementById('wiz');
const steps = wiz.querySelectorAll('md-step');
const msg = document.getElementById('msg');
const fix = document.getElementById('fix');
// Stand-in for your real validation: the card is bad until the user fixes it.
let cardValid = false;
const validateStep = (i) => i !== 2 || cardValid;
// Offer Continue only when it can succeed: disabled exactly while the CURRENT
// step is flagged. next-disabled applies wherever the user is, so recompute it
// on every change — otherwise a valid step is stranded behind a dead button.
const syncNav = () => { wiz.nextDisabled = !!steps[wiz.active]?.error; };
wiz.addEventListener('mdBeforeChange', (e) => {
const { index, previous } = e.detail;
if (index > previous && !validateStep(previous)) {
e.preventDefault(); // blocks the move
steps[previous].error = true; // and says why
steps[previous].errorText = 'Enter a valid card number';
msg.textContent = 'Blocked — mdBeforeChange was cancelled, so Continue is disabled until it passes.';
fix.hidden = false;
syncNav();
} else {
steps[previous].error = false; // clear it once it passes
}
});
wiz.addEventListener('mdStepChange', (e) => {
msg.textContent = e.detail.index > e.detail.previous
? 'That step passed, so the move went through.'
: 'Going back is never gated.';
syncNav();
});
fix.addEventListener('mdClick', () => {
cardValid = true;
steps[2].error = false;
fix.hidden = true;
msg.textContent = 'Card accepted — Continue is live again.';
syncNav();
});
syncNav();
</script>import { useRef, useState } from 'react';
import { MdButton, MdStep, MdStepper } from '@awc-ui/react';
export function Checkout() {
const wiz = useRef(null);
const steps = useRef([]);
const [cardValid, setCardValid] = useState(false);
const [showFix, setShowFix] = useState(false);
const [msg, setMsg] = useState(
'Press Continue. Cart and Shipping advance normally — Payment is the one that fails.',
);
// Stand-in for your real validation: the card is bad until the user fixes it.
const validateStep = (i) => i !== 2 || cardValid;
// Offer Continue only when it can succeed: disabled exactly while the CURRENT
// step is flagged. next-disabled applies wherever the user is, so it has to be
// recomputed on every change.
const syncNav = () => {
const current = steps.current[wiz.current?.active ?? 0];
wiz.current.nextDisabled = !!current?.error;
};
function onBeforeChange(e) {
const { index, previous } = e.detail;
const step = steps.current[previous];
if (index > previous && !validateStep(previous)) {
e.preventDefault(); // blocks the move
step.error = true; // and says why
step.errorText = 'Enter a valid card number';
setMsg('Blocked — mdBeforeChange was cancelled, so Continue is disabled until it passes.');
setShowFix(true);
syncNav();
} else {
step.error = false;
}
}
function onStepChange(e) {
setMsg(e.detail.index > e.detail.previous
? 'That step passed, so the move went through.'
: 'Going back is never gated.');
syncNav();
}
function onFix() {
setCardValid(true);
steps.current[2].error = false;
setShowFix(false);
setMsg('Card accepted — Continue is live again.');
syncNav();
}
return (
<MdStepper
ref={wiz}
label="Checkout"
onMdBeforeChange={onBeforeChange}
onMdStepChange={onStepChange}
>
<MdStep ref={(el) => (steps.current[0] = el)} label="Cart" />
<MdStep ref={(el) => (steps.current[1] = el)} label="Shipping" />
<MdStep ref={(el) => (steps.current[2] = el)} label="Payment" />
<MdStep ref={(el) => (steps.current[3] = el)} label="Review" />
<div slot="content">
<span>{msg}</span>
{showFix && (
<MdButton variant="tonal" size="sm" onMdClick={onFix}>Use a valid card</MdButton>
)}
</div>
</MdStepper>
);
}import { Component, ElementRef, QueryList, ViewChild, ViewChildren } from '@angular/core';
@Component({
selector: 'app-checkout',
template: `
<md-stepper
#wiz
label="Checkout"
(mdBeforeChange)="onBeforeChange($event)"
(mdStepChange)="onStepChange($event)"
>
<md-step #step label="Cart"></md-step>
<md-step #step label="Shipping"></md-step>
<md-step #step label="Payment"></md-step>
<md-step #step label="Review"></md-step>
<div slot="content">
<span>{{ msg }}</span>
<md-button *ngIf="showFix" variant="tonal" size="sm" (mdClick)="onFix()">
Use a valid card
</md-button>
</div>
</md-stepper>
`,
})
export class CheckoutComponent {
@ViewChild('wiz') wiz!: ElementRef<HTMLMdStepperElement>;
@ViewChildren('step') steps!: QueryList<ElementRef<HTMLMdStepElement>>;
cardValid = false;
showFix = false;
msg = 'Press Continue. Cart and Shipping advance normally — Payment is the one that fails.';
// Stand-in for your real validation: the card is bad until the user fixes it.
private validateStep = (i: number) => i !== 2 || this.cardValid;
private step(i: number) { return this.steps.get(i)!.nativeElement; }
// Offer Continue only when it can succeed: disabled exactly while the CURRENT
// step is flagged. next-disabled applies wherever the user is, so recompute it.
private syncNav() {
const el = this.wiz.nativeElement;
el.nextDisabled = !!this.step(el.active).error;
}
onBeforeChange(e: CustomEvent<{ index: number; previous: number }>) {
const { index, previous } = e.detail;
const step = this.step(previous);
if (index > previous && !this.validateStep(previous)) {
e.preventDefault(); // blocks the move
step.error = true; // and says why
step.errorText = 'Enter a valid card number';
this.msg = 'Blocked — mdBeforeChange was cancelled, so Continue is disabled until it passes.';
this.showFix = true;
this.syncNav();
} else {
step.error = false;
}
}
onStepChange(e: CustomEvent<{ index: number; previous: number }>) {
this.msg = e.detail.index > e.detail.previous
? 'That step passed, so the move went through.'
: 'Going back is never gated.';
this.syncNav();
}
onFix() {
this.cardValid = true;
this.step(2).error = false;
this.showFix = false;
this.msg = 'Card accepted — Continue is live again.';
this.syncNav();
}
}<script setup lang="ts">
import { ref } from 'vue';
const wiz = ref<HTMLMdStepperElement>();
const steps = ref<HTMLMdStepElement[]>([]);
const cardValid = ref(false);
const showFix = ref(false);
const msg = ref('Press Continue. Cart and Shipping advance normally — Payment is the one that fails.');
// Stand-in for your real validation: the card is bad until the user fixes it.
const validateStep = (i: number) => i !== 2 || cardValid.value;
// Offer Continue only when it can succeed: disabled exactly while the CURRENT
// step is flagged. next-disabled applies wherever the user is, so recompute it.
function syncNav() {
const el = wiz.value!;
el.nextDisabled = !!steps.value[el.active]?.error;
}
function onBeforeChange(e: CustomEvent) {
const { index, previous } = e.detail;
const step = steps.value[previous];
if (index > previous && !validateStep(previous)) {
e.preventDefault(); // blocks the move
step.error = true; // and says why
step.errorText = 'Enter a valid card number';
msg.value = 'Blocked — mdBeforeChange was cancelled, so Continue is disabled until it passes.';
showFix.value = true;
syncNav();
} else {
step.error = false;
}
}
function onStepChange(e: CustomEvent) {
msg.value = e.detail.index > e.detail.previous
? 'That step passed, so the move went through.'
: 'Going back is never gated.';
syncNav();
}
function onFix() {
cardValid.value = true;
steps.value[2].error = false;
showFix.value = false;
msg.value = 'Card accepted — Continue is live again.';
syncNav();
}
</script>
<template>
<md-stepper
ref="wiz"
label="Checkout"
@mdBeforeChange="onBeforeChange"
@mdStepChange="onStepChange"
>
<md-step :ref="(el) => (steps[0] = el)" label="Cart" />
<md-step :ref="(el) => (steps[1] = el)" label="Shipping" />
<md-step :ref="(el) => (steps[2] = el)" label="Payment" />
<md-step :ref="(el) => (steps[3] = el)" label="Review" />
<div slot="content">
<span>{{ msg }}</span>
<md-button v-if="showFix" variant="tonal" size="sm" @mdClick="onFix">
Use a valid card
</md-button>
</div>
</md-stepper>
</template><script lang="ts">
let wiz: HTMLMdStepperElement;
let steps: HTMLMdStepElement[] = [];
let cardValid = false;
let showFix = false;
let msg = 'Press Continue. Cart and Shipping advance normally — Payment is the one that fails.';
// Stand-in for your real validation: the card is bad until the user fixes it.
const validateStep = (i: number) => i !== 2 || cardValid;
// Offer Continue only when it can succeed: disabled exactly while the CURRENT
// step is flagged. next-disabled applies wherever the user is, so recompute it.
function syncNav() {
wiz.nextDisabled = !!steps[wiz.active]?.error;
}
function onBeforeChange(e: CustomEvent) {
const { index, previous } = e.detail;
const step = steps[previous];
if (index > previous && !validateStep(previous)) {
e.preventDefault(); // blocks the move
step.error = true; // and says why
step.errorText = 'Enter a valid card number';
msg = 'Blocked — mdBeforeChange was cancelled, so Continue is disabled until it passes.';
showFix = true;
syncNav();
} else {
step.error = false;
}
}
function onStepChange(e: CustomEvent) {
msg = e.detail.index > e.detail.previous
? 'That step passed, so the move went through.'
: 'Going back is never gated.';
syncNav();
}
function onFix() {
cardValid = true;
steps[2].error = false;
showFix = false;
msg = 'Card accepted — Continue is live again.';
syncNav();
}
</script>
<md-stepper
bind:this={wiz}
label="Checkout"
on:mdBeforeChange={onBeforeChange}
on:mdStepChange={onStepChange}
>
<md-step bind:this={steps[0]} label="Cart" />
<md-step bind:this={steps[1]} label="Shipping" />
<md-step bind:this={steps[2]} label="Payment" />
<md-step bind:this={steps[3]} label="Review" />
<div slot="content">
<span>{msg}</span>
{#if showFix}
<md-button variant="tonal" size="sm" on:mdClick={onFix}>Use a valid card</md-button>
{/if}
</div>
</md-stepper>Pair a cancellation with a visible, announced reason — the in-step surface is
md-step’s error + error-text. A silent block is
invisible to screen-reader users.
mdBeforeChange does not fire for a direct active assignment, which makes
active the authoritative commit path after async work:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-stepper id="wiz" label="Checkout">
<md-step label="Cart"></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<md-step label="Review"></md-step>
<div slot="content">…</div>
</md-stepper>
<script type="module">
const wiz = document.getElementById('wiz');
wiz.addEventListener('mdBeforeChange', async (e) => {
const target = e.detail.index;
e.preventDefault(); // nothing commits yet
wiz.loading = true; // spinner on Continue, Back disabled
// Direct assignment does NOT re-fire mdBeforeChange, so this cannot loop.
if (await save()) wiz.active = target;
wiz.loading = false;
});
</script>import { useRef } from 'react';
import { MdStep, MdStepper } from '@awc-ui/react';
export function Checkout({ save }) {
const wiz = useRef(null);
async function onBeforeChange(e) {
const target = e.detail.index;
e.preventDefault(); // nothing commits yet
wiz.current.loading = true; // spinner on Continue, Back disabled
// Direct assignment does NOT re-fire mdBeforeChange, so this cannot loop.
if (await save()) wiz.current.active = target;
wiz.current.loading = false;
}
return (
<MdStepper ref={wiz} label="Checkout" onMdBeforeChange={onBeforeChange}>
<MdStep label="Cart" />
<MdStep label="Shipping" />
<MdStep label="Payment" />
<MdStep label="Review" />
<div slot="content">…</div>
</MdStepper>
);
}import { Component, ElementRef, ViewChild } from '@angular/core';
@Component({
selector: 'app-checkout',
template: `
<md-stepper #wiz label="Checkout" (mdBeforeChange)="onBeforeChange($event)">
<md-step label="Cart"></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<md-step label="Review"></md-step>
<div slot="content">…</div>
</md-stepper>
`,
})
export class CheckoutComponent {
@ViewChild('wiz') wizRef!: ElementRef<HTMLMdStepperElement>;
async onBeforeChange(e: CustomEvent<{ index: number; previous: number }>) {
const wiz = this.wizRef.nativeElement;
const target = e.detail.index;
e.preventDefault(); // nothing commits yet
wiz.loading = true; // spinner on Continue, Back disabled
// Direct assignment does NOT re-fire mdBeforeChange, so this cannot loop.
if (await this.save()) wiz.active = target;
wiz.loading = false;
}
async save() { return true; }
}<script setup lang="ts">
import { ref } from 'vue';
const wiz = ref<HTMLMdStepperElement>();
async function onBeforeChange(e: CustomEvent) {
const target = e.detail.index;
e.preventDefault(); // nothing commits yet
wiz.value!.loading = true; // spinner on Continue, Back disabled
// Direct assignment does NOT re-fire mdBeforeChange, so this cannot loop.
if (await save()) wiz.value!.active = target;
wiz.value!.loading = false;
}
</script>
<template>
<md-stepper ref="wiz" label="Checkout" @mdBeforeChange="onBeforeChange">
<md-step label="Cart" />
<md-step label="Shipping" />
<md-step label="Payment" />
<md-step label="Review" />
<div slot="content">…</div>
</md-stepper>
</template><script lang="ts">
let wiz: HTMLMdStepperElement;
async function onBeforeChange(e: CustomEvent) {
const target = e.detail.index;
e.preventDefault(); // nothing commits yet
wiz.loading = true; // spinner on Continue, Back disabled
// Direct assignment does NOT re-fire mdBeforeChange, so this cannot loop.
if (await save()) wiz.active = target;
wiz.loading = false;
}
</script>
<md-stepper bind:this={wiz} label="Checkout" on:mdBeforeChange={onBeforeChange}>
<md-step label="Cart" />
<md-step label="Shipping" />
<md-step label="Payment" />
<md-step label="Review" />
<div slot="content">…</div>
</md-stepper>Use next-disabled for the simpler, declarative case — it disables the built-in
Continue / Finish button (not Back, not step clicks) so you can bind it to the
current step’s form validity.
auto-complete is on by default: advancing marks the step you leave as
completed, so the progress line and check marks just work.
Both steppers below start on step 1 — press Continue a few times in each. The first leaves check marks behind; the second never does, because completion is yours to confirm:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-stepper id="w" auto-complete="false" label="Checkout">
<md-step label="Cart"></md-step>
<md-step label="Payment"></md-step>
<md-step label="Review"></md-step>
</md-stepper>
<script type="module">
const w = document.getElementById('w');
const steps = w.querySelectorAll('md-step');
// Mark the step completed only once the server has actually accepted it.
w.addEventListener('mdStepChange', async (e) => {
const { previous } = e.detail;
if (await saveToServer(previous)) steps[previous].completed = true;
});
</script>import { useRef } from 'react';
import { MdStep, MdStepper } from '@awc-ui/react';
export function Checkout({ saveToServer }) {
const steps = useRef([]);
async function onStepChange(e) {
const { previous } = e.detail;
if (await saveToServer(previous)) steps.current[previous].completed = true;
}
return (
<MdStepper label="Checkout" autoComplete={false} onMdStepChange={onStepChange}>
<MdStep ref={(el) => (steps.current[0] = el)} label="Cart" />
<MdStep ref={(el) => (steps.current[1] = el)} label="Payment" />
<MdStep ref={(el) => (steps.current[2] = el)} label="Review" />
</MdStepper>
);
}import { Component, ElementRef, QueryList, ViewChildren } from '@angular/core';
@Component({
selector: 'app-checkout',
template: `
<md-stepper label="Checkout" auto-complete="false" (mdStepChange)="onStepChange($event)">
<md-step #step label="Cart"></md-step>
<md-step #step label="Payment"></md-step>
<md-step #step label="Review"></md-step>
</md-stepper>
`,
})
export class CheckoutComponent {
@ViewChildren('step') steps!: QueryList<ElementRef<HTMLMdStepElement>>;
async onStepChange(e: CustomEvent<{ previous: number }>) {
const { previous } = e.detail;
if (await this.saveToServer(previous)) {
this.steps.get(previous)!.nativeElement.completed = true;
}
}
async saveToServer(_i: number) { return true; }
}<script setup lang="ts">
import { ref } from 'vue';
const steps = ref<HTMLMdStepElement[]>([]);
async function onStepChange(e: CustomEvent) {
const { previous } = e.detail;
if (await saveToServer(previous)) steps.value[previous].completed = true;
}
</script>
<template>
<md-stepper label="Checkout" auto-complete="false" @mdStepChange="onStepChange">
<md-step :ref="(el) => (steps[0] = el)" label="Cart" />
<md-step :ref="(el) => (steps[1] = el)" label="Payment" />
<md-step :ref="(el) => (steps[2] = el)" label="Review" />
</md-stepper>
</template><script lang="ts">
let steps: HTMLMdStepElement[] = [];
async function onStepChange(e: CustomEvent) {
const { previous } = e.detail;
if (await saveToServer(previous)) steps[previous].completed = true;
}
</script>
<md-stepper label="Checkout" auto-complete="false" on:mdStepChange={onStepChange}>
<md-step bind:this={steps[0]} label="Cart" />
<md-step bind:this={steps[1]} label="Payment" />
<md-step bind:this={steps[2]} label="Review" />
</md-stepper>| Mode | Behaviour |
|---|---|
linear (default) | Steps must be done in order; future steps are unreachable until the prior ones are done or optional |
non-linear | Any step can be selected |
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-stepper id="st-nonlinear" mode="non-linear" active="2" label="Non-linear" style="inline-size: 100%;">
<md-step label="Account" completed editable></md-step>
<md-step label="Shipping" completed editable></md-step>
<md-step label="Payment"></md-step>
<div slot="content">
<div data-panel="0" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Account</div>
<div>Jump straight back here — an editable step stays clickable.</div>
</div>
<div data-panel="1" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Shipping</div>
<div>Also editable, so revisiting it keeps the later progress.</div>
</div>
<div data-panel="2" style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Payment</div>
<div>The step you are on. Click a completed header to go back.</div>
</div>
</div>
</md-stepper>
<script type="module">
var stepper = document.getElementById('st-nonlinear');
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-nonlinear [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
</script>import { useEffect, useRef } from 'react';
import { MdStep, MdStepper } from '@awc-ui/react';
export function Demo() {
const stepperRef = useRef(null);
useEffect(() => {
const stepper = stepperRef.current;
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-nonlinear [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
}, []);
return (
<>
<MdStepper id="st-nonlinear" ref={stepperRef} mode="non-linear" active="2" label="Non-linear" style={{ inlineSize: '100%' }}>
<MdStep label="Account" completed editable></MdStep>
<MdStep label="Shipping" completed editable></MdStep>
<MdStep label="Payment"></MdStep>
<div slot="content">
<div data-panel="0" hidden style={{ padding: '4px 0 8px', font: 'var(--md-sys-typescale-body-medium)', color: 'var(--md-sys-color-on-surface-variant)' }}>
<div style={{ font: 'var(--md-sys-typescale-title-small)', color: 'var(--md-sys-color-on-surface)' }}>Account</div>
<div>Jump straight back here — an editable step stays clickable.</div>
</div>
<div data-panel="1" hidden style={{ padding: '4px 0 8px', font: 'var(--md-sys-typescale-body-medium)', color: 'var(--md-sys-color-on-surface-variant)' }}>
<div style={{ font: 'var(--md-sys-typescale-title-small)', color: 'var(--md-sys-color-on-surface)' }}>Shipping</div>
<div>Also editable, so revisiting it keeps the later progress.</div>
</div>
<div data-panel="2" style={{ padding: '4px 0 8px', font: 'var(--md-sys-typescale-body-medium)', color: 'var(--md-sys-color-on-surface-variant)' }}>
<div style={{ font: 'var(--md-sys-typescale-title-small)', color: 'var(--md-sys-color-on-surface)' }}>Payment</div>
<div>The step you are on. Click a completed header to go back.</div>
</div>
</div>
</MdStepper>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
// app.component.ts
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';
@Component({
selector: 'app-demo',
templateUrl: './app.component.html',
})
export class DemoComponent implements AfterViewInit {
@ViewChild('stepper') stepperRef!: ElementRef;
ngAfterViewInit() {
const stepper = this.stepperRef.nativeElement;
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-nonlinear [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
}
}
<!-- app.component.html -->
<md-stepper id="st-nonlinear" #stepper mode="non-linear" active="2" label="Non-linear" style="inline-size: 100%;">
<md-step label="Account" completed editable></md-step>
<md-step label="Shipping" completed editable></md-step>
<md-step label="Payment"></md-step>
<div slot="content">
<div data-panel="0" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Account</div>
<div>Jump straight back here — an editable step stays clickable.</div>
</div>
<div data-panel="1" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Shipping</div>
<div>Also editable, so revisiting it keeps the later progress.</div>
</div>
<div data-panel="2" style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Payment</div>
<div>The step you are on. Click a completed header to go back.</div>
</div>
</div>
</md-stepper><script setup>
import { onMounted, ref } from 'vue';
import '@awc-ui/core/define';
const stepperRef = ref(null);
onMounted(() => {
const stepper = stepperRef.value;
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-nonlinear [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
});
</script>
<template>
<md-stepper id="st-nonlinear" ref="stepperRef" mode="non-linear" active="2" label="Non-linear" style="inline-size: 100%;">
<md-step label="Account" completed editable></md-step>
<md-step label="Shipping" completed editable></md-step>
<md-step label="Payment"></md-step>
<div slot="content">
<div data-panel="0" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Account</div>
<div>Jump straight back here — an editable step stays clickable.</div>
</div>
<div data-panel="1" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Shipping</div>
<div>Also editable, so revisiting it keeps the later progress.</div>
</div>
<div data-panel="2" style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Payment</div>
<div>The step you are on. Click a completed header to go back.</div>
</div>
</div>
</md-stepper>
</template><script>
import { onMount } from 'svelte';
import '@awc-ui/core/define';
let stepperRef;
onMount(() => {
const stepper = stepperRef;
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-nonlinear [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
});
</script>
<md-stepper id="st-nonlinear" bind:this={stepperRef} mode="non-linear" active="2" label="Non-linear" style="inline-size: 100%;">
<md-step label="Account" completed editable></md-step>
<md-step label="Shipping" completed editable></md-step>
<md-step label="Payment"></md-step>
<div slot="content">
<div data-panel="0" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Account</div>
<div>Jump straight back here — an editable step stays clickable.</div>
</div>
<div data-panel="1" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Shipping</div>
<div>Also editable, so revisiting it keeps the later progress.</div>
</div>
<div data-panel="2" style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Payment</div>
<div>The step you are on. Click a completed header to go back.</div>
</div>
</div>
</md-stepper>orientation="horizontal" (default) keeps the step row clean — put the active
panel in the stepper’s content slot and swap it on mdStepChange.
orientation="vertical" renders each step’s own slotted content in an expanding
panel with built-in Back / Continue.
Panels can live in either place, and the stepper picks the layout for you:
| Where you put the panel | What happens |
|---|---|
One <div slot="content"> on the stepper | The shared region under the row — you swap it on mdStepChange |
Inside each <md-step> | Vertical: expands in place. Horizontal: spans the full width under the row |
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<!-- horizontal, but each step carries its own panel -->
<md-stepper active="1" label="Checkout">
<md-step label="Cart">3 items · $128.40</md-step>
<md-step label="Shipping" description="Address and method">Pick a delivery method.</md-step>
<md-step label="Payment">Card details.</md-step>
<md-step label="Review">Confirm and pay.</md-step>
</md-stepper>import { MdStep, MdStepper } from '@awc-ui/react';
export function Demo() {
return (
<>
<!-- horizontal, but each step carries its own panel -->
<MdStepper active="1" label="Checkout">
<MdStep label="Cart">3 items · $128.40</MdStep>
<MdStep label="Shipping" description="Address and method">Pick a delivery method.</MdStep>
<MdStep label="Payment">Card details.</MdStep>
<MdStep label="Review">Confirm and pay.</MdStep>
</MdStepper>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<!-- horizontal, but each step carries its own panel -->
<md-stepper active="1" label="Checkout">
<md-step label="Cart">3 items · $128.40</md-step>
<md-step label="Shipping" description="Address and method">Pick a delivery method.</md-step>
<md-step label="Payment">Card details.</md-step>
<md-step label="Review">Confirm and pay.</md-step>
</md-stepper><script setup>
import '@awc-ui/core/define';
</script>
<template>
<!-- horizontal, but each step carries its own panel -->
<md-stepper active="1" label="Checkout">
<md-step label="Cart">3 items · $128.40</md-step>
<md-step label="Shipping" description="Address and method">Pick a delivery method.</md-step>
<md-step label="Payment">Card details.</md-step>
<md-step label="Review">Confirm and pay.</md-step>
</md-stepper>
</template><script>
import '@awc-ui/core/define';
</script>
<!-- horizontal, but each step carries its own panel -->
<md-stepper active="1" label="Checkout">
<md-step label="Cart">3 items · $128.40</md-step>
<md-step label="Shipping" description="Address and method">Pick a delivery method.</md-step>
<md-step label="Payment">Card details.</md-step>
<md-step label="Review">Confirm and pay.</md-step>
</md-stepper><!-- index.html <head> — the icon font the components draw from -->
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=swap">
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-stepper orientation="vertical" label="Deploy" style="inline-size: 100%;">
<md-step label="Connect repo" icon="cable">Pick the repository to deploy.</md-step>
<md-step label="Configure build" icon="tune">Set the build command.</md-step>
<md-step label="Deploy" icon="rocket_launch">Ship it.</md-step>
</md-stepper>// Icons need the Material Symbols stylesheet in index.html — see Installation.
import { MdStep, MdStepper } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdStepper orientation="vertical" label="Deploy" style={{ inlineSize: '100%' }}>
<MdStep label="Connect repo" icon="cable">Pick the repository to deploy.</MdStep>
<MdStep label="Configure build" icon="tune">Set the build command.</MdStep>
<MdStep label="Deploy" icon="rocket_launch">Ship it.</MdStep>
</MdStepper>
</>
);
}// Icons need the Material Symbols stylesheet in index.html — see Installation.
// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-stepper orientation="vertical" label="Deploy" style="inline-size: 100%;">
<md-step label="Connect repo" icon="cable">Pick the repository to deploy.</md-step>
<md-step label="Configure build" icon="tune">Set the build command.</md-step>
<md-step label="Deploy" icon="rocket_launch">Ship it.</md-step>
</md-stepper><script setup>
// Icons need the Material Symbols stylesheet in index.html — see Installation.
import '@awc-ui/core/define';
</script>
<template>
<md-stepper orientation="vertical" label="Deploy" style="inline-size: 100%;">
<md-step label="Connect repo" icon="cable">Pick the repository to deploy.</md-step>
<md-step label="Configure build" icon="tune">Set the build command.</md-step>
<md-step label="Deploy" icon="rocket_launch">Ship it.</md-step>
</md-stepper>
</template><script>
// Icons need the Material Symbols stylesheet in index.html — see Installation.
import '@awc-ui/core/define';
</script>
<md-stepper orientation="vertical" label="Deploy" style="inline-size: 100%;">
<md-step label="Connect repo" icon="cable">Pick the repository to deploy.</md-step>
<md-step label="Configure build" icon="tune">Set the build command.</md-step>
<md-step label="Deploy" icon="rocket_launch">Ship it.</md-step>
</md-stepper>indicator="numbered" (default) draws numbered circles; indicator="dot" draws
minimal dots. icon on a step overrides the glyph.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-stepper id="st-dot" indicator="dot" label="Dot stepper" style="inline-size: 100%;">
<md-step label="Welcome"></md-step>
<md-step label="Profile"></md-step>
<md-step label="Preferences"></md-step>
<md-step label="Done"></md-step>
<div slot="content">
<div data-panel="0" style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Welcome</div>
<div>Panel for “Welcome”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
<div data-panel="1" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Profile</div>
<div>Panel for “Profile”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
<div data-panel="2" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Preferences</div>
<div>Panel for “Preferences”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
<div data-panel="3" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Done</div>
<div>Panel for “Done”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
</div>
</md-stepper>
<script type="module">
var stepper = document.getElementById('st-dot');
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-dot [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
</script>import { useEffect, useRef } from 'react';
import { MdStep, MdStepper } from '@awc-ui/react';
export function Demo() {
const stepperRef = useRef(null);
useEffect(() => {
const stepper = stepperRef.current;
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-dot [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
}, []);
return (
<>
<MdStepper id="st-dot" ref={stepperRef} indicator="dot" label="Dot stepper" style={{ inlineSize: '100%' }}>
<MdStep label="Welcome"></MdStep>
<MdStep label="Profile"></MdStep>
<MdStep label="Preferences"></MdStep>
<MdStep label="Done"></MdStep>
<div slot="content">
<div data-panel="0" style={{ padding: '4px 0 8px', font: 'var(--md-sys-typescale-body-medium)', color: 'var(--md-sys-color-on-surface-variant)' }}>
<div style={{ font: 'var(--md-sys-typescale-title-small)', color: 'var(--md-sys-color-on-surface)' }}>Welcome</div>
<div>Panel for “Welcome”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
<div data-panel="1" hidden style={{ padding: '4px 0 8px', font: 'var(--md-sys-typescale-body-medium)', color: 'var(--md-sys-color-on-surface-variant)' }}>
<div style={{ font: 'var(--md-sys-typescale-title-small)', color: 'var(--md-sys-color-on-surface)' }}>Profile</div>
<div>Panel for “Profile”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
<div data-panel="2" hidden style={{ padding: '4px 0 8px', font: 'var(--md-sys-typescale-body-medium)', color: 'var(--md-sys-color-on-surface-variant)' }}>
<div style={{ font: 'var(--md-sys-typescale-title-small)', color: 'var(--md-sys-color-on-surface)' }}>Preferences</div>
<div>Panel for “Preferences”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
<div data-panel="3" hidden style={{ padding: '4px 0 8px', font: 'var(--md-sys-typescale-body-medium)', color: 'var(--md-sys-color-on-surface-variant)' }}>
<div style={{ font: 'var(--md-sys-typescale-title-small)', color: 'var(--md-sys-color-on-surface)' }}>Done</div>
<div>Panel for “Done”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
</div>
</MdStepper>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
// app.component.ts
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';
@Component({
selector: 'app-demo',
templateUrl: './app.component.html',
})
export class DemoComponent implements AfterViewInit {
@ViewChild('stepper') stepperRef!: ElementRef;
ngAfterViewInit() {
const stepper = this.stepperRef.nativeElement;
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-dot [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
}
}
<!-- app.component.html -->
<md-stepper id="st-dot" #stepper indicator="dot" label="Dot stepper" style="inline-size: 100%;">
<md-step label="Welcome"></md-step>
<md-step label="Profile"></md-step>
<md-step label="Preferences"></md-step>
<md-step label="Done"></md-step>
<div slot="content">
<div data-panel="0" style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Welcome</div>
<div>Panel for “Welcome”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
<div data-panel="1" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Profile</div>
<div>Panel for “Profile”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
<div data-panel="2" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Preferences</div>
<div>Panel for “Preferences”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
<div data-panel="3" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Done</div>
<div>Panel for “Done”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
</div>
</md-stepper><script setup>
import { onMounted, ref } from 'vue';
import '@awc-ui/core/define';
const stepperRef = ref(null);
onMounted(() => {
const stepper = stepperRef.value;
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-dot [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
});
</script>
<template>
<md-stepper id="st-dot" ref="stepperRef" indicator="dot" label="Dot stepper" style="inline-size: 100%;">
<md-step label="Welcome"></md-step>
<md-step label="Profile"></md-step>
<md-step label="Preferences"></md-step>
<md-step label="Done"></md-step>
<div slot="content">
<div data-panel="0" style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Welcome</div>
<div>Panel for “Welcome”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
<div data-panel="1" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Profile</div>
<div>Panel for “Profile”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
<div data-panel="2" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Preferences</div>
<div>Panel for “Preferences”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
<div data-panel="3" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Done</div>
<div>Panel for “Done”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
</div>
</md-stepper>
</template><script>
import { onMount } from 'svelte';
import '@awc-ui/core/define';
let stepperRef;
onMount(() => {
const stepper = stepperRef;
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-dot [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
});
</script>
<md-stepper id="st-dot" bind:this={stepperRef} indicator="dot" label="Dot stepper" style="inline-size: 100%;">
<md-step label="Welcome"></md-step>
<md-step label="Profile"></md-step>
<md-step label="Preferences"></md-step>
<md-step label="Done"></md-step>
<div slot="content">
<div data-panel="0" style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Welcome</div>
<div>Panel for “Welcome”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
<div data-panel="1" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Profile</div>
<div>Panel for “Profile”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
<div data-panel="2" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Preferences</div>
<div>Panel for “Preferences”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
<div data-panel="3" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Done</div>
<div>Panel for “Done”. The dot indicator drops the numbers; the content still swaps.</div>
</div>
</div>
</md-stepper>variant="mobile" swaps the whole header row for a compact bar — Back, a
centered progress readout (dots for indicator="dot", otherwise a “Step N of M”
caption), and Continue / Finish. The step headers are hidden, so the active
panel must go in the content slot.
A step is completed, error, optional, disabled or plain — and the state
shows in the indicator glyph and the announced text, never colour alone.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-stepper mode="non-linear" active="3" label="States" style="inline-size: 100%;">
<md-step label="Account" completed editable></md-step>
<md-step label="Shipping" error error-text="Postcode not recognised"></md-step>
<md-step label="Gift wrap" optional></md-step>
<md-step label="Payment"></md-step>
<md-step label="Review" disabled></md-step>
<div slot="content">Completed, error, optional, current and disabled.</div>
</md-stepper>import { MdStep, MdStepper } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdStepper mode="non-linear" active="3" label="States" style={{ inlineSize: '100%' }}>
<MdStep label="Account" completed editable></MdStep>
<MdStep label="Shipping" error errorText="Postcode not recognised"></MdStep>
<MdStep label="Gift wrap" optional></MdStep>
<MdStep label="Payment"></MdStep>
<MdStep label="Review" disabled></MdStep>
<div slot="content">Completed, error, optional, current and disabled.</div>
</MdStepper>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-stepper mode="non-linear" active="3" label="States" style="inline-size: 100%;">
<md-step label="Account" completed editable></md-step>
<md-step label="Shipping" error error-text="Postcode not recognised"></md-step>
<md-step label="Gift wrap" optional></md-step>
<md-step label="Payment"></md-step>
<md-step label="Review" disabled></md-step>
<div slot="content">Completed, error, optional, current and disabled.</div>
</md-stepper><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-stepper mode="non-linear" active="3" label="States" style="inline-size: 100%;">
<md-step label="Account" completed editable></md-step>
<md-step label="Shipping" error error-text="Postcode not recognised"></md-step>
<md-step label="Gift wrap" optional></md-step>
<md-step label="Payment"></md-step>
<md-step label="Review" disabled></md-step>
<div slot="content">Completed, error, optional, current and disabled.</div>
</md-stepper>
</template><script>
import '@awc-ui/core/define';
</script>
<md-stepper mode="non-linear" active="3" label="States" style="inline-size: 100%;">
<md-step label="Account" completed editable></md-step>
<md-step label="Shipping" error error-text="Postcode not recognised"></md-step>
<md-step label="Gift wrap" optional></md-step>
<md-step label="Payment"></md-step>
<md-step label="Review" disabled></md-step>
<div slot="content">Completed, error, optional, current and disabled.</div>
</md-stepper><!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="inline-size:100%;max-inline-size:420px;">
<md-stepper id="st-mobile" variant="mobile" active="1" label="Checkout" style="inline-size: 100%;">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">
<div data-panel="0" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Account</div>
<div>Signed in as ada@example.com.</div>
</div>
<div data-panel="1" style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Shipping</div>
<div>12 Ampere Way, London — standard delivery.</div>
</div>
<div data-panel="2" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Payment</div>
<div>Card ending 4242. Continue becomes Finish on the last step.</div>
</div>
</div>
</md-stepper>
</div>
<script type="module">
var stepper = document.getElementById('st-mobile');
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-mobile [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
</script>import { useEffect, useRef } from 'react';
import { MdStep, MdStepper } from '@awc-ui/react';
export function Demo() {
const stepperRef = useRef(null);
useEffect(() => {
const stepper = stepperRef.current;
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-mobile [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
}, []);
return (
<>
<div style={{ inlineSize: '100%', maxInlineSize: '420px' }}>
<MdStepper id="st-mobile" ref={stepperRef} variant="mobile" active="1" label="Checkout" style={{ inlineSize: '100%' }}>
<MdStep label="Account" completed></MdStep>
<MdStep label="Shipping"></MdStep>
<MdStep label="Payment"></MdStep>
<div slot="content">
<div data-panel="0" hidden style={{ padding: '4px 0 8px', font: 'var(--md-sys-typescale-body-medium)', color: 'var(--md-sys-color-on-surface-variant)' }}>
<div style={{ font: 'var(--md-sys-typescale-title-small)', color: 'var(--md-sys-color-on-surface)' }}>Account</div>
<div>Signed in as ada@example.com.</div>
</div>
<div data-panel="1" style={{ padding: '4px 0 8px', font: 'var(--md-sys-typescale-body-medium)', color: 'var(--md-sys-color-on-surface-variant)' }}>
<div style={{ font: 'var(--md-sys-typescale-title-small)', color: 'var(--md-sys-color-on-surface)' }}>Shipping</div>
<div>12 Ampere Way, London — standard delivery.</div>
</div>
<div data-panel="2" hidden style={{ padding: '4px 0 8px', font: 'var(--md-sys-typescale-body-medium)', color: 'var(--md-sys-color-on-surface-variant)' }}>
<div style={{ font: 'var(--md-sys-typescale-title-small)', color: 'var(--md-sys-color-on-surface)' }}>Payment</div>
<div>Card ending 4242. Continue becomes Finish on the last step.</div>
</div>
</div>
</MdStepper>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
// app.component.ts
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';
@Component({
selector: 'app-demo',
templateUrl: './app.component.html',
})
export class DemoComponent implements AfterViewInit {
@ViewChild('stepper') stepperRef!: ElementRef;
ngAfterViewInit() {
const stepper = this.stepperRef.nativeElement;
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-mobile [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
}
}
<!-- app.component.html -->
<div style="inline-size:100%;max-inline-size:420px;">
<md-stepper id="st-mobile" #stepper variant="mobile" active="1" label="Checkout" style="inline-size: 100%;">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">
<div data-panel="0" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Account</div>
<div>Signed in as ada@example.com.</div>
</div>
<div data-panel="1" style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Shipping</div>
<div>12 Ampere Way, London — standard delivery.</div>
</div>
<div data-panel="2" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Payment</div>
<div>Card ending 4242. Continue becomes Finish on the last step.</div>
</div>
</div>
</md-stepper>
</div><script setup>
import { onMounted, ref } from 'vue';
import '@awc-ui/core/define';
const stepperRef = ref(null);
onMounted(() => {
const stepper = stepperRef.value;
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-mobile [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
});
</script>
<template>
<div style="inline-size:100%;max-inline-size:420px;">
<md-stepper id="st-mobile" ref="stepperRef" variant="mobile" active="1" label="Checkout" style="inline-size: 100%;">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">
<div data-panel="0" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Account</div>
<div>Signed in as ada@example.com.</div>
</div>
<div data-panel="1" style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Shipping</div>
<div>12 Ampere Way, London — standard delivery.</div>
</div>
<div data-panel="2" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Payment</div>
<div>Card ending 4242. Continue becomes Finish on the last step.</div>
</div>
</div>
</md-stepper>
</div>
</template><script>
import { onMount } from 'svelte';
import '@awc-ui/core/define';
let stepperRef;
onMount(() => {
const stepper = stepperRef;
var panels = Array.prototype.slice.call(document.querySelectorAll('#st-mobile [data-panel]'));
// A horizontal stepper has ONE content slot — the consumer swaps what is in it.
// (Vertical steppers are different: content lives inside each md-step.)
var show = function (i) {
panels.forEach(function (p) { p.hidden = Number(p.dataset.panel) !== i; });
};
stepper.addEventListener('mdStepChange', function (e) { show(e.detail.index); });
show(Number(stepper.getAttribute('active') || 0));
});
</script>
<div style="inline-size:100%;max-inline-size:420px;">
<md-stepper id="st-mobile" bind:this={stepperRef} variant="mobile" active="1" label="Checkout" style="inline-size: 100%;">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">
<div data-panel="0" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Account</div>
<div>Signed in as ada@example.com.</div>
</div>
<div data-panel="1" style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Shipping</div>
<div>12 Ampere Way, London — standard delivery.</div>
</div>
<div data-panel="2" hidden style="padding: 4px 0 8px; font: var(--md-sys-typescale-body-medium); color: var(--md-sys-color-on-surface-variant);">
<div style="font: var(--md-sys-typescale-title-small); color: var(--md-sys-color-on-surface);">Payment</div>
<div>Card ending 4242. Continue becomes Finish on the last step.</div>
</div>
</div>
</md-stepper>
</div>nav (default true) renders the built-in Back / Continue — under each active
step when vertical, as a single bar below the stepper when horizontal. Set
nav="false" and you drive the flow with next(), prev() and goTo(index)
(all of which still emit the cancelable mdBeforeChange); reset() returns to
the first step and clears completed / error state.
The demo below is live — the buttons are ordinary md-buttons calling the
stepper’s methods:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-stepper id="w" nav="false" label="Checkout">
<md-step label="Cart"></md-step>
<md-step label="Payment"></md-step>
<md-step label="Review"></md-step>
<div slot="content">…</div>
</md-stepper>
<md-button id="back">Back</md-button>
<md-button id="next" variant="filled">Continue</md-button>
<script type="module">
const w = document.getElementById('w');
// next() / prev() / goTo() all still emit the cancelable mdBeforeChange,
// so your validation gate keeps working with your own buttons.
document.getElementById('back').addEventListener('mdClick', () => w.prev());
document.getElementById('next').addEventListener('mdClick', () => w.next());
</script>import { useRef } from 'react';
import { MdButton, MdStep, MdStepper } from '@awc-ui/react';
export function Checkout() {
const wiz = useRef(null);
return (
<>
<MdStepper ref={wiz} nav={false} label="Checkout">
<MdStep label="Cart" />
<MdStep label="Payment" />
<MdStep label="Review" />
<div slot="content">…</div>
</MdStepper>
<MdButton onMdClick={() => wiz.current?.prev()}>Back</MdButton>
<MdButton variant="filled" onMdClick={() => wiz.current?.next()}>Continue</MdButton>
</>
);
}import { Component, ElementRef, ViewChild } from '@angular/core';
@Component({
selector: 'app-checkout',
template: `
<md-stepper #wiz nav="false" label="Checkout">
<md-step label="Cart"></md-step>
<md-step label="Payment"></md-step>
<md-step label="Review"></md-step>
<div slot="content">…</div>
</md-stepper>
<md-button (mdClick)="wiz.prev()">Back</md-button>
<md-button variant="filled" (mdClick)="wiz.next()">Continue</md-button>
`,
})
export class CheckoutComponent {
@ViewChild('wiz') wiz!: ElementRef<HTMLMdStepperElement>;
}<script setup lang="ts">
import { ref } from 'vue';
const wiz = ref<HTMLMdStepperElement>();
</script>
<template>
<md-stepper ref="wiz" nav="false" label="Checkout">
<md-step label="Cart" />
<md-step label="Payment" />
<md-step label="Review" />
<div slot="content">…</div>
</md-stepper>
<md-button @mdClick="wiz?.prev()">Back</md-button>
<md-button variant="filled" @mdClick="wiz?.next()">Continue</md-button>
</template><script lang="ts">
let wiz: HTMLMdStepperElement;
</script>
<md-stepper bind:this={wiz} nav="false" label="Checkout">
<md-step label="Cart" />
<md-step label="Payment" />
<md-step label="Review" />
<div slot="content">…</div>
</md-stepper>
<md-button on:mdClick={() => wiz.prev()}>Back</md-button>
<md-button variant="filled" on:mdClick={() => wiz.next()}>Continue</md-button>With nav="false" the button labels are yours too — next-label, back-label
and finish-label only name the built-in buttons.
lazy mounts only the active vertical step’s content panel; inactive panels
leave layout and the accessibility tree (no collapse animation). Worth it for
wizards with heavy per-step forms.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<!-- Plain HTML has no conditional rendering, so lazy is as far as it goes.
Remove the child yourself if it must truly tear down. -->
<md-stepper id="w" orientation="vertical" lazy label="Deploy">
<md-step label="Connect repo">…</md-step>
<md-step label="Configure build">…</md-step>
</md-stepper>
<script type="module">
const w = document.getElementById('w');
w.addEventListener('mdStepChange', (e) => {
// Full teardown: drop the previous step's children entirely.
w.querySelectorAll('md-step')[e.detail.previous].replaceChildren();
});
</script>import { useState } from 'react';
import { MdStep, MdStepper } from '@awc-ui/react';
export function Deploy() {
const [active, setActive] = useState(0);
// Rendering the panel only when it is active tears the child down for real —
// lazy alone leaves slotted elements connected in the light DOM.
return (
<MdStepper
orientation="vertical"
label="Deploy"
active={active}
onMdStepChange={(e) => setActive(e.detail.index)}
>
<MdStep label="Connect repo">{active === 0 && <RepoPicker />}</MdStep>
<MdStep label="Configure build">{active === 1 && <BuildForm />}</MdStep>
<MdStep label="Deploy">{active === 2 && <DeployPanel />}</MdStep>
</MdStepper>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-deploy',
template: `
<md-stepper orientation="vertical" label="Deploy" [active]="active"
(mdStepChange)="active = $event.detail.index">
<md-step label="Connect repo">
<app-repo-picker *ngIf="active === 0"></app-repo-picker>
</md-step>
<md-step label="Configure build">
<app-build-form *ngIf="active === 1"></app-build-form>
</md-step>
</md-stepper>
`,
})
export class DeployComponent {
active = 0;
}<script setup lang="ts">
import { ref } from 'vue';
const active = ref(0);
</script>
<template>
<md-stepper
orientation="vertical"
label="Deploy"
:active="active"
@mdStepChange="active = $event.detail.index"
>
<md-step label="Connect repo"><RepoPicker v-if="active === 0" /></md-step>
<md-step label="Configure build"><BuildForm v-if="active === 1" /></md-step>
</md-stepper>
</template><script lang="ts">
let active = 0;
</script>
<md-stepper
orientation="vertical"
label="Deploy"
{active}
on:mdStepChange={(e) => (active = e.detail.index)}
>
<md-step label="Connect repo">{#if active === 0}<RepoPicker />{/if}</md-step>
<md-step label="Configure build">{#if active === 1}<BuildForm />{/if}</md-step>
</md-stepper><!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-stepper label="Progression" step-word="Étape" of-word="sur"
completed-word="terminée" current-word="actuelle" error-word="erreur"
optional-word="Facultatif" next-label="Continuer" back-label="Retour"
finish-label="Terminer" style="inline-size: 100%;">
<md-step label="Panier"></md-step>
<md-step label="Livraison" optional></md-step>
<md-step label="Paiement"></md-step>
<div slot="content">Votre panier contient 3 articles.</div>
</md-stepper>import { MdStep, MdStepper } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdStepper label="Progression" stepWord="Étape" ofWord="sur"
completedWord="terminée" currentWord="actuelle" errorWord="erreur"
optionalWord="Facultatif" nextLabel="Continuer" backLabel="Retour"
finishLabel="Terminer" style={{ inlineSize: '100%' }}>
<MdStep label="Panier"></MdStep>
<MdStep label="Livraison" optional></MdStep>
<MdStep label="Paiement"></MdStep>
<div slot="content">Votre panier contient 3 articles.</div>
</MdStepper>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-stepper label="Progression" step-word="Étape" of-word="sur"
completed-word="terminée" current-word="actuelle" error-word="erreur"
optional-word="Facultatif" next-label="Continuer" back-label="Retour"
finish-label="Terminer" style="inline-size: 100%;">
<md-step label="Panier"></md-step>
<md-step label="Livraison" optional></md-step>
<md-step label="Paiement"></md-step>
<div slot="content">Votre panier contient 3 articles.</div>
</md-stepper><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-stepper label="Progression" step-word="Étape" of-word="sur"
completed-word="terminée" current-word="actuelle" error-word="erreur"
optional-word="Facultatif" next-label="Continuer" back-label="Retour"
finish-label="Terminer" style="inline-size: 100%;">
<md-step label="Panier"></md-step>
<md-step label="Livraison" optional></md-step>
<md-step label="Paiement"></md-step>
<div slot="content">Votre panier contient 3 articles.</div>
</md-stepper>
</template><script>
import '@awc-ui/core/define';
</script>
<md-stepper label="Progression" step-word="Étape" of-word="sur"
completed-word="terminée" current-word="actuelle" error-word="erreur"
optional-word="Facultatif" next-label="Continuer" back-label="Retour"
finish-label="Terminer" style="inline-size: 100%;">
<md-step label="Panier"></md-step>
<md-step label="Livraison" optional></md-step>
<md-step label="Paiement"></md-step>
<div slot="content">Votre panier contient 3 articles.</div>
</md-stepper>The optional-word is rendered by each md-step marked optional, so that
string is translated here on the parent, not on the step. Per-step strings
(label, description, error-text, accessible-name) are translated on the
step. The assembled order (“Step 2 of 4”) is fixed — locales needing a different
order may need the wording adjusted to fit.
| Event | Cancelable | Detail | Fires |
|---|---|---|---|
mdBeforeChange | yes | { index, previous } | Before a user-driven move commits — not on a direct active assignment |
mdStepChange | no | { index, previous } | After the move commits |
mdComplete | no | void | Continue pressed on the last step |
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-stepper id="wiz" label="Checkout progress">
<md-step label="Account"></md-step>
<md-step label="Payment"></md-step>
<md-step label="Review"></md-step>
<div slot="content" id="panel">Account details…</div>
</md-stepper>
<script type="module">
const wiz = document.getElementById('wiz');
// Cancelable, before the move commits — this is the validation gate.
wiz.addEventListener('mdBeforeChange', (e) => {
const { index, previous } = e.detail;
if (index > previous && !validateStep(previous)) e.preventDefault();
});
// After the move commits — swap the shared panel here.
wiz.addEventListener('mdStepChange', (e) => renderPanel(e.detail.index));
// Continue pressed on the last step.
wiz.addEventListener('mdComplete', () => submitOrder());
</script>import { useState } from 'react';
import { MdStep, MdStepper } from '@awc-ui/react';
export function Checkout({ panels, validateStep, submitOrder }) {
const [step, setStep] = useState(0);
return (
<MdStepper
label="Checkout progress"
onMdBeforeChange={(e) => {
const { index, previous } = e.detail; // cancelable
if (index > previous && !validateStep(previous)) e.preventDefault();
}}
onMdStepChange={(e) => setStep(e.detail.index)} // after the move
onMdComplete={() => submitOrder()} // last step finished
>
<MdStep label="Account" />
<MdStep label="Payment" />
<MdStep label="Review" />
<div slot="content">{panels[step]}</div>
</MdStepper>
);
}import { Component } from '@angular/core';
@Component({
selector: 'app-checkout',
template: `
<md-stepper
label="Checkout progress"
(mdBeforeChange)="onBeforeChange($event)"
(mdStepChange)="step = $event.detail.index"
(mdComplete)="submitOrder()"
>
<md-step label="Account"></md-step>
<md-step label="Payment"></md-step>
<md-step label="Review"></md-step>
<div slot="content">{{ panels[step] }}</div>
</md-stepper>
`,
})
export class CheckoutComponent {
step = 0;
panels = ['Account details…', 'Payment details…', 'Review your order'];
onBeforeChange(e: CustomEvent<{ index: number; previous: number }>) {
const { index, previous } = e.detail; // cancelable
if (index > previous && !this.validateStep(previous)) e.preventDefault();
}
validateStep(_i: number) { return true; }
submitOrder() { /* … */ }
}<script setup lang="ts">
import { ref } from 'vue';
const step = ref(0);
const panels = ['Account details…', 'Payment details…', 'Review your order'];
function onBeforeChange(e: CustomEvent) {
const { index, previous } = e.detail; // cancelable
if (index > previous && !validateStep(previous)) e.preventDefault();
}
</script>
<template>
<md-stepper
label="Checkout progress"
@mdBeforeChange="onBeforeChange"
@mdStepChange="step = $event.detail.index"
@mdComplete="submitOrder"
>
<md-step label="Account" />
<md-step label="Payment" />
<md-step label="Review" />
<div slot="content">{{ panels[step] }}</div>
</md-stepper>
</template><script lang="ts">
let step = 0;
const panels = ['Account details…', 'Payment details…', 'Review your order'];
function onBeforeChange(e: CustomEvent) {
const { index, previous } = e.detail; // cancelable
if (index > previous && !validateStep(previous)) e.preventDefault();
}
</script>
<md-stepper
label="Checkout progress"
on:mdBeforeChange={onBeforeChange}
on:mdStepChange={(e) => (step = e.detail.index)}
on:mdComplete={submitOrder}
>
<md-step label="Account" />
<md-step label="Payment" />
<md-step label="Review" />
<div slot="content">{panels[step]}</div>
</md-stepper>| Property | Attribute | Type | Default | Reflects |
|---|---|---|---|---|
orientation | orientation | 'horizontal' | 'vertical' | 'horizontal' | Yes |
indicator | indicator | 'numbered' | 'dot' | 'numbered' | Yes |
variant | variant | 'default' | 'mobile' | 'default' | Yes |
mode | mode | 'linear' | 'non-linear' | 'linear' | Yes |
active | active | number | 0 | Yes |
autoComplete | auto-complete | boolean | true | — |
nav | nav | boolean | true | — |
nextDisabled | next-disabled | boolean | false | — |
loading | loading | boolean | false | — |
lazy | lazy | boolean | false | — |
label | label | string | 'Progress' | — |
stepWord | step-word | string | 'Step' | — |
ofWord | of-word | string | 'of' | — |
completedWord | completed-word | string | 'completed' | — |
currentWord | current-word | string | 'current' | — |
errorWord | error-word | string | 'error' | — |
optionalWord | optional-word | string | 'Optional' | — |
nextLabel | next-label | string | 'Continue' | — |
backLabel | back-label | string | 'Back' | — |
finishLabel | finish-label | string | 'Finish' | — |
density | density | 0 | -1 | -2 | -3 | -4 | 0 | Yes |
| Method | Parameters |
|---|---|
next() | none |
prev() | none |
goTo() | index: number |
reset() | none |
| Slot | Description |
|---|---|
(default) | — |
content | Active-step panel for horizontal / mobile steppers — rendered |
Override on the host element for per-instance theming:
| Property | Description |
|---|---|
--md-stepper-gap | Spacing between steps (vertical orientation only). |
--md-stepper-step-count | — |
Style internal elements through shadow DOM with ::part():
| Part | Description |
|---|---|
list | — |
content | — |
nav | — |
mobile-nav | — |
mobile-progress | — |
A stepper is composed from one sub-component, documented here rather than on its
own page: it means nothing outside md-stepper, which owns the active index,
completion and the navigation events.
md-stepOne step in the stepper — an indicator (number, dot, check or error glyph), a label, an optional description, and the step’s content.
active and completed are stepper-managed. The stepper’s active index
drives active, and its auto-complete drives completed. Set completed
yourself only after turning auto-complete="false" off on the parent —
otherwise the two fight.mdStepClick, mdStepNext and mdStepBack are internal plumbing behind
the header and the built-in action buttons; the stepper consumes (and stops)
them. Application logic belongs on the stepper’s mdBeforeChange /
mdStepChange / mdComplete.error + error-text is the in-step validation surface — pair it with
cancelling the stepper’s mdBeforeChange so the block has a visible,
announced reason. error-text replaces description while the error is set.mode="linear", a finished step is only revisitable if you set
editable — which, with auto-complete on, also preserves downstream
completion when the user edits in place. Revisiting a non-editable step
restarts progress from there.optional renders the optional-word supplied by the stepper, so that
string is translated once on the parent rather than per step.| Property | Attribute | Type | Default | Reflects |
|---|---|---|---|---|
label | label | string | '' | — |
description | description | string | '' | — |
completed | completed | boolean | false | Yes |
active | active | boolean | false | Yes |
error | error | boolean | false | Yes |
errorText | error-text | string | '' | — |
disabled | disabled | boolean | false | Yes |
optional | optional | boolean | false | Yes |
editable | editable | boolean | false | Yes |
icon | icon | string | '' | — |
completedIcon | completed-icon | string | 'check' | — |
errorIcon | error-icon | string | 'priority_high' | — |
accessibleName | accessible-name | string | — | — |
hideActions | hide-actions | boolean | false | Yes |
density | density | 0 | -1 | -2 | -3 | -4 | 0 | Yes |
| Slot | Description |
|---|---|
(default) | — |
Override on the host element for per-instance theming:
| Property | Description |
|---|---|
--md-step-indicator-size | Bubble diameter (32px; 28px compact) |
--md-step-indicator-ring | Hover / ripple ring thickness around the |
--md-step-indicator-color | Pending bubble background |
--md-step-indicator-text | Pending bubble text |
--md-step-active-color | Active bubble background |
--md-step-active-text | Active bubble text |
--md-step-active-halo-color | Soft halo around the active bubble |
--md-step-completed-color | Completed bubble background |
--md-step-completed-text | Completed bubble text |
--md-step-error-color | Error bubble background |
--md-step-error-text | Error bubble text |
--md-step-connector-color | Inactive connector track |
--md-step-connector-filled-color | Filled connector |
--md-step-connector-thickness | Connector thickness (4px) |
--md-step-connector-duration | Connector fill duration |
--md-step-label-color | Resting label colour |
--md-step-label-active-color | Active label colour |
--md-step-description-color | Supporting / optional caption colour |
--md-step-state-layer-color | Hover / focus / press overlay |
--md-step-expand-duration | Vertical panel expand duration |
--md-step-collapse-duration | Vertical panel collapse duration |
--md-step-idx | — |
Style internal elements through shadow DOM with ::part():
| Part | Description |
|---|---|
bubble | — |
dot | — |
connector connector-leading | — |
inner | — |
indicator | — |
state-layer | — |
text | — |
label | — |
optional | — |
description | — |
connector connector-trailing | — |
content | — |
actions | — |
label names the progress landmark. step-word / of-word /
completed-word / current-word / error-word / optional-word are
assembled into each step’s announcement (“Step 2 of 4, current”).mdBeforeChange should be paired with a visible, announced reason
— a silent block is invisible to screen-reader users.nav="false" you own the navigation buttons and their accessible
names.| Key | Action |
|---|---|
Tab | The step headers (when reachable), then the navigation buttons |
← / → | Move between reachable headers on a horizontal stepper (mirrored under dir="rtl") |
↑ / ↓ | Move between headers on a vertical stepper |
Home / End | First / last reachable step |
Enter / Space | Select the focused step — still subject to mdBeforeChange |
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-stepper mode="non-linear" active="1" label="Checkout progress" style="inline-size: 100%;">
<md-step label="Account" description="Signed in as ada@example.com" completed editable></md-step>
<md-step label="Shipping" description="Where it goes"></md-step>
<md-step label="Gift wrap" description="Skippable" optional></md-step>
<md-step label="Payment" description="Card details"></md-step>
<div slot="content">Tab to the step headers, then arrow between them.</div>
</md-stepper>import { MdStep, MdStepper } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdStepper mode="non-linear" active="1" label="Checkout progress" style={{ inlineSize: '100%' }}>
<MdStep label="Account" description="Signed in as ada@example.com" completed editable></MdStep>
<MdStep label="Shipping" description="Where it goes"></MdStep>
<MdStep label="Gift wrap" description="Skippable" optional></MdStep>
<MdStep label="Payment" description="Card details"></MdStep>
<div slot="content">Tab to the step headers, then arrow between them.</div>
</MdStepper>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-stepper mode="non-linear" active="1" label="Checkout progress" style="inline-size: 100%;">
<md-step label="Account" description="Signed in as ada@example.com" completed editable></md-step>
<md-step label="Shipping" description="Where it goes"></md-step>
<md-step label="Gift wrap" description="Skippable" optional></md-step>
<md-step label="Payment" description="Card details"></md-step>
<div slot="content">Tab to the step headers, then arrow between them.</div>
</md-stepper><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-stepper mode="non-linear" active="1" label="Checkout progress" style="inline-size: 100%;">
<md-step label="Account" description="Signed in as ada@example.com" completed editable></md-step>
<md-step label="Shipping" description="Where it goes"></md-step>
<md-step label="Gift wrap" description="Skippable" optional></md-step>
<md-step label="Payment" description="Card details"></md-step>
<div slot="content">Tab to the step headers, then arrow between them.</div>
</md-stepper>
</template><script>
import '@awc-ui/core/define';
</script>
<md-stepper mode="non-linear" active="1" label="Checkout progress" style="inline-size: 100%;">
<md-step label="Account" description="Signed in as ada@example.com" completed editable></md-step>
<md-step label="Shipping" description="Where it goes"></md-step>
<md-step label="Gift wrap" description="Skippable" optional></md-step>
<md-step label="Payment" description="Card details"></md-step>
<div slot="content">Tab to the step headers, then arrow between them.</div>
</md-stepper>RTL — the horizontal layout, connectors and navigation buttons mirror under
dir="rtl", and the horizontal arrow keys invert with them. See
RTL.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display:grid;gap:24px;inline-size:100%;">
<div dir="ltr">
<md-stepper active="1" label="Checkout" style="inline-size: 100%;">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">Content</div>
</md-stepper>
</div>
<div dir="rtl">
<md-stepper active="1" label="الدفع" step-word="خطوة" of-word="من" style="inline-size: 100%;">
<md-step label="الحساب" completed></md-step>
<md-step label="الشحن"></md-step>
<md-step label="الدفع"></md-step>
<div slot="content">المحتوى</div>
</md-stepper>
</div>
</div>import { MdStep, MdStepper } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ display: 'grid', gap: '24px', inlineSize: '100%' }}>
<div dir="ltr">
<MdStepper active="1" label="Checkout" style={{ inlineSize: '100%' }}>
<MdStep label="Account" completed></MdStep>
<MdStep label="Shipping"></MdStep>
<MdStep label="Payment"></MdStep>
<div slot="content">Content</div>
</MdStepper>
</div>
<div dir="rtl">
<MdStepper active="1" label="الدفع" stepWord="خطوة" ofWord="من" style={{ inlineSize: '100%' }}>
<MdStep label="الحساب" completed></MdStep>
<MdStep label="الشحن"></MdStep>
<MdStep label="الدفع"></MdStep>
<div slot="content">المحتوى</div>
</MdStepper>
</div>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display:grid;gap:24px;inline-size:100%;">
<div dir="ltr">
<md-stepper active="1" label="Checkout" style="inline-size: 100%;">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">Content</div>
</md-stepper>
</div>
<div dir="rtl">
<md-stepper active="1" label="الدفع" step-word="خطوة" of-word="من" style="inline-size: 100%;">
<md-step label="الحساب" completed></md-step>
<md-step label="الشحن"></md-step>
<md-step label="الدفع"></md-step>
<div slot="content">المحتوى</div>
</md-stepper>
</div>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div style="display:grid;gap:24px;inline-size:100%;">
<div dir="ltr">
<md-stepper active="1" label="Checkout" style="inline-size: 100%;">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">Content</div>
</md-stepper>
</div>
<div dir="rtl">
<md-stepper active="1" label="الدفع" step-word="خطوة" of-word="من" style="inline-size: 100%;">
<md-step label="الحساب" completed></md-step>
<md-step label="الشحن"></md-step>
<md-step label="الدفع"></md-step>
<div slot="content">المحتوى</div>
</md-stepper>
</div>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div style="display:grid;gap:24px;inline-size:100%;">
<div dir="ltr">
<md-stepper active="1" label="Checkout" style="inline-size: 100%;">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">Content</div>
</md-stepper>
</div>
<div dir="rtl">
<md-stepper active="1" label="الدفع" step-word="خطوة" of-word="من" style="inline-size: 100%;">
<md-step label="الحساب" completed></md-step>
<md-step label="الشحن"></md-step>
<md-step label="الدفع"></md-step>
<div slot="content">المحتوى</div>
</md-stepper>
</div>
</div>Density — density="-1…-4" compacts indicators, connectors and content, and
overrides an inherited data-density ancestor. See
Density.
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display:grid;grid-template-columns:auto 1fr;gap:18px 16px;align-items:center;inline-size:100%;">
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">0</span>
<md-stepper density="0" active="1" label="d0" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-1</span>
<md-stepper density="-1" active="1" label="d-1" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-2</span>
<md-stepper density="-2" active="1" label="d-2" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-3</span>
<md-stepper density="-3" active="1" label="d-3" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-4</span>
<md-stepper density="-4" active="1" label="d-4" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
</div>import { MdStep, MdStepper } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ display: 'grid', gridTemplateColumns: 'auto 1fr', gap: '18px 16px', alignItems: 'center', inlineSize: '100%' }}>
<span style={{ inlineSize: '2rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>0</span>
<MdStepper density="0" active="1" label="d0" nav="false" style={{ inlineSize: '100%' }}><MdStep label="Account" completed></MdStep><MdStep label="Shipping"></MdStep><MdStep label="Payment"></MdStep></MdStepper>
<span style={{ inlineSize: '2rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>-1</span>
<MdStepper density="-1" active="1" label="d-1" nav="false" style={{ inlineSize: '100%' }}><MdStep label="Account" completed></MdStep><MdStep label="Shipping"></MdStep><MdStep label="Payment"></MdStep></MdStepper>
<span style={{ inlineSize: '2rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>-2</span>
<MdStepper density="-2" active="1" label="d-2" nav="false" style={{ inlineSize: '100%' }}><MdStep label="Account" completed></MdStep><MdStep label="Shipping"></MdStep><MdStep label="Payment"></MdStep></MdStepper>
<span style={{ inlineSize: '2rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>-3</span>
<MdStepper density="-3" active="1" label="d-3" nav="false" style={{ inlineSize: '100%' }}><MdStep label="Account" completed></MdStep><MdStep label="Shipping"></MdStep><MdStep label="Payment"></MdStep></MdStepper>
<span style={{ inlineSize: '2rem', opacity: '.65', fontSize: '.75rem', fontFamily: 'ui-monospace,monospace' }}>-4</span>
<MdStepper density="-4" active="1" label="d-4" nav="false" style={{ inlineSize: '100%' }}><MdStep label="Account" completed></MdStep><MdStep label="Shipping"></MdStep><MdStep label="Payment"></MdStep></MdStepper>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display:grid;grid-template-columns:auto 1fr;gap:18px 16px;align-items:center;inline-size:100%;">
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">0</span>
<md-stepper density="0" active="1" label="d0" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-1</span>
<md-stepper density="-1" active="1" label="d-1" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-2</span>
<md-stepper density="-2" active="1" label="d-2" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-3</span>
<md-stepper density="-3" active="1" label="d-3" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-4</span>
<md-stepper density="-4" active="1" label="d-4" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div style="display:grid;grid-template-columns:auto 1fr;gap:18px 16px;align-items:center;inline-size:100%;">
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">0</span>
<md-stepper density="0" active="1" label="d0" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-1</span>
<md-stepper density="-1" active="1" label="d-1" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-2</span>
<md-stepper density="-2" active="1" label="d-2" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-3</span>
<md-stepper density="-3" active="1" label="d-3" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-4</span>
<md-stepper density="-4" active="1" label="d-4" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div style="display:grid;grid-template-columns:auto 1fr;gap:18px 16px;align-items:center;inline-size:100%;">
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">0</span>
<md-stepper density="0" active="1" label="d0" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-1</span>
<md-stepper density="-1" active="1" label="d-1" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-2</span>
<md-stepper density="-2" active="1" label="d-2" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-3</span>
<md-stepper density="-3" active="1" label="d-3" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
<span style="inline-size:2rem;opacity:.65;font-size:.75rem;font-family:ui-monospace,monospace;">-4</span>
<md-stepper density="-4" active="1" label="d-4" nav="false" style="inline-size:100%;"><md-step label="Account" completed></md-step><md-step label="Shipping"></md-step><md-step label="Payment"></md-step></md-stepper>
</div>i18n — translate every *-word and *-label on the stepper, plus each
step’s label, description and error-text. See the
i18n section above.
The stepper owns one property — the gap between steps. Everything visible
belongs to md-step, and those properties cascade from the stepper.
| Custom property | On | Purpose |
|---|---|---|
--md-stepper-gap | md-stepper | Space between steps |
--md-step-indicator-size | md-step | Indicator diameter |
--md-step-indicator-color / -indicator-text / -indicator-ring | md-step | Resting indicator fill, number, ring |
--md-step-active-color / -active-text / -active-halo-color | md-step | Current step indicator |
--md-step-completed-color / -completed-text | md-step | Completed indicator |
--md-step-error-color / -error-text | md-step | Error indicator |
--md-step-connector-color / -connector-filled-color / -connector-thickness | md-step | The rule between steps |
--md-step-label-color / -label-active-color | md-step | Label text |
--md-step-description-color | md-step | Description text |
--md-step-state-layer-color | md-step | Hover / focus / press overlay |
--md-step-expand-duration / -collapse-duration / -connector-duration | md-step | Motion |
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display:grid;gap:24px;inline-size:100%;">
<md-stepper active="1" label="Tonal" style="inline-size:100%; --md-step-active-color: var(--md-sys-color-tertiary); --md-step-active-text: var(--md-sys-color-on-tertiary); --md-step-completed-color: var(--md-sys-color-tertiary-container); --md-step-completed-text: var(--md-sys-color-on-tertiary-container); --md-step-connector-filled-color: var(--md-sys-color-tertiary);">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">Tonal palette</div>
</md-stepper>
<md-stepper active="1" label="Big indicators" style="inline-size:100%; --md-stepper-gap: 32px; --md-step-indicator-size: 44px; --md-step-connector-thickness: 3px;">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">Larger indicators and a thicker connector</div>
</md-stepper>
<md-stepper active="1" label="Branded" style="inline-size:100%; --md-step-active-color: #7c4dff; --md-step-active-text: #ffffff; --md-step-completed-color: #7c4dff; --md-step-completed-text: #ffffff; --md-step-connector-filled-color: #7c4dff; --md-step-label-active-color: #311b92;">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">Branded</div>
</md-stepper>
</div>import { MdStep, MdStepper } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ display: 'grid', gap: '24px', inlineSize: '100%' }}>
<MdStepper active="1" label="Tonal" style={{ inlineSize: '100%', '--md-step-active-color': 'var(--md-sys-color-tertiary)', '--md-step-active-text': 'var(--md-sys-color-on-tertiary)', '--md-step-completed-color': 'var(--md-sys-color-tertiary-container)', '--md-step-completed-text': 'var(--md-sys-color-on-tertiary-container)', '--md-step-connector-filled-color': 'var(--md-sys-color-tertiary)' }}>
<MdStep label="Account" completed></MdStep>
<MdStep label="Shipping"></MdStep>
<MdStep label="Payment"></MdStep>
<div slot="content">Tonal palette</div>
</MdStepper>
<MdStepper active="1" label="Big indicators" style={{ inlineSize: '100%', '--md-stepper-gap': '32px', '--md-step-indicator-size': '44px', '--md-step-connector-thickness': '3px' }}>
<MdStep label="Account" completed></MdStep>
<MdStep label="Shipping"></MdStep>
<MdStep label="Payment"></MdStep>
<div slot="content">Larger indicators and a thicker connector</div>
</MdStepper>
<MdStepper active="1" label="Branded" style={{ inlineSize: '100%', '--md-step-active-color': '#7c4dff', '--md-step-active-text': '#ffffff', '--md-step-completed-color': '#7c4dff', '--md-step-completed-text': '#ffffff', '--md-step-connector-filled-color': '#7c4dff', '--md-step-label-active-color': '#311b92' }}>
<MdStep label="Account" completed></MdStep>
<MdStep label="Shipping"></MdStep>
<MdStep label="Payment"></MdStep>
<div slot="content">Branded</div>
</MdStepper>
</div>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display:grid;gap:24px;inline-size:100%;">
<md-stepper active="1" label="Tonal" style="inline-size:100%; --md-step-active-color: var(--md-sys-color-tertiary); --md-step-active-text: var(--md-sys-color-on-tertiary); --md-step-completed-color: var(--md-sys-color-tertiary-container); --md-step-completed-text: var(--md-sys-color-on-tertiary-container); --md-step-connector-filled-color: var(--md-sys-color-tertiary);">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">Tonal palette</div>
</md-stepper>
<md-stepper active="1" label="Big indicators" style="inline-size:100%; --md-stepper-gap: 32px; --md-step-indicator-size: 44px; --md-step-connector-thickness: 3px;">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">Larger indicators and a thicker connector</div>
</md-stepper>
<md-stepper active="1" label="Branded" style="inline-size:100%; --md-step-active-color: #7c4dff; --md-step-active-text: #ffffff; --md-step-completed-color: #7c4dff; --md-step-completed-text: #ffffff; --md-step-connector-filled-color: #7c4dff; --md-step-label-active-color: #311b92;">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">Branded</div>
</md-stepper>
</div><script setup>
import '@awc-ui/core/define';
</script>
<template>
<div style="display:grid;gap:24px;inline-size:100%;">
<md-stepper active="1" label="Tonal" style="inline-size:100%; --md-step-active-color: var(--md-sys-color-tertiary); --md-step-active-text: var(--md-sys-color-on-tertiary); --md-step-completed-color: var(--md-sys-color-tertiary-container); --md-step-completed-text: var(--md-sys-color-on-tertiary-container); --md-step-connector-filled-color: var(--md-sys-color-tertiary);">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">Tonal palette</div>
</md-stepper>
<md-stepper active="1" label="Big indicators" style="inline-size:100%; --md-stepper-gap: 32px; --md-step-indicator-size: 44px; --md-step-connector-thickness: 3px;">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">Larger indicators and a thicker connector</div>
</md-stepper>
<md-stepper active="1" label="Branded" style="inline-size:100%; --md-step-active-color: #7c4dff; --md-step-active-text: #ffffff; --md-step-completed-color: #7c4dff; --md-step-completed-text: #ffffff; --md-step-connector-filled-color: #7c4dff; --md-step-label-active-color: #311b92;">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">Branded</div>
</md-stepper>
</div>
</template><script>
import '@awc-ui/core/define';
</script>
<div style="display:grid;gap:24px;inline-size:100%;">
<md-stepper active="1" label="Tonal" style="inline-size:100%; --md-step-active-color: var(--md-sys-color-tertiary); --md-step-active-text: var(--md-sys-color-on-tertiary); --md-step-completed-color: var(--md-sys-color-tertiary-container); --md-step-completed-text: var(--md-sys-color-on-tertiary-container); --md-step-connector-filled-color: var(--md-sys-color-tertiary);">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">Tonal palette</div>
</md-stepper>
<md-stepper active="1" label="Big indicators" style="inline-size:100%; --md-stepper-gap: 32px; --md-step-indicator-size: 44px; --md-step-connector-thickness: 3px;">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">Larger indicators and a thicker connector</div>
</md-stepper>
<md-stepper active="1" label="Branded" style="inline-size:100%; --md-step-active-color: #7c4dff; --md-step-active-text: #ffffff; --md-step-completed-color: #7c4dff; --md-step-completed-text: #ffffff; --md-step-connector-filled-color: #7c4dff; --md-step-label-active-color: #311b92;">
<md-step label="Account" completed></md-step>
<md-step label="Shipping"></md-step>
<md-step label="Payment"></md-step>
<div slot="content">Branded</div>
</md-stepper>
</div>Every default resolves through an md-sys-color role, so a stepper that sets no
custom properties follows the theme on its own:
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<md-stepper active="1" label="Untouched defaults" style="inline-size: 100%;">
<md-step label="Account" description="Signed in" completed></md-step>
<md-step label="Shipping" description="Where it goes"></md-step>
<md-step label="Payment" description="Card details"></md-step>
<div slot="content">Switch this page between light and dark.</div>
</md-stepper>import { MdStep, MdStepper } from '@awc-ui/react';
export function Demo() {
return (
<>
<MdStepper active="1" label="Untouched defaults" style={{ inlineSize: '100%' }}>
<MdStep label="Account" description="Signed in" completed></MdStep>
<MdStep label="Shipping" description="Where it goes"></MdStep>
<MdStep label="Payment" description="Card details"></MdStep>
<div slot="content">Switch this page between light and dark.</div>
</MdStepper>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<md-stepper active="1" label="Untouched defaults" style="inline-size: 100%;">
<md-step label="Account" description="Signed in" completed></md-step>
<md-step label="Shipping" description="Where it goes"></md-step>
<md-step label="Payment" description="Card details"></md-step>
<div slot="content">Switch this page between light and dark.</div>
</md-stepper><script setup>
import '@awc-ui/core/define';
</script>
<template>
<md-stepper active="1" label="Untouched defaults" style="inline-size: 100%;">
<md-step label="Account" description="Signed in" completed></md-step>
<md-step label="Shipping" description="Where it goes"></md-step>
<md-step label="Payment" description="Card details"></md-step>
<div slot="content">Switch this page between light and dark.</div>
</md-stepper>
</template><script>
import '@awc-ui/core/define';
</script>
<md-stepper active="1" label="Untouched defaults" style="inline-size: 100%;">
<md-step label="Account" description="Signed in" completed></md-step>
<md-step label="Shipping" description="Where it goes"></md-step>
<md-step label="Payment" description="Card details"></md-step>
<div slot="content">Switch this page between light and dark.</div>
</md-stepper>| Part | On | Element |
|---|---|---|
list | md-stepper | The step header row / column |
content | md-stepper | The shared content slot wrapper |
nav | md-stepper | The built-in Back / Continue row |
mobile-nav / mobile-progress | md-stepper | variant="mobile" bar and its readout |
indicator / bubble / dot / inner | md-step | The indicator, by shape |
connector connector-leading / connector connector-trailing | md-step | The rules either side |
text / label / description / optional | md-step | The text block |
state-layer | md-step | Hover / focus / press overlay |
content / actions | md-step | Vertical-mode panel and its buttons |
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<style>
.parts-stepper::part(nav) { justify-content: flex-end; }
.parts-stepper::part(list) { padding-block-end: 8px; border-block-end: 1px solid var(--md-sys-color-outline-variant); }
.parts-stepper md-step::part(label) { text-transform: uppercase; letter-spacing: .08em; font-size: 11px; }
.parts-stepper md-step::part(description) { font-style: italic; }
</style>
<md-stepper class="parts-stepper" active="1" label="Styled parts" style="inline-size: 100%;">
<md-step label="Account" description="Signed in" completed></md-step>
<md-step label="Shipping" description="Where it goes"></md-step>
<md-step label="Payment" description="Card details"></md-step>
<div slot="content">The nav row is right-aligned via ::part(nav).</div>
</md-stepper>import { MdStep, MdStepper } from '@awc-ui/react';
export function Demo() {
return (
<>
<style>
.parts-stepper::part(nav) { justify-content: flex-end; }
.parts-stepper::part(list) { padding-block-end: 8px; border-block-end: 1px solid var(--md-sys-color-outline-variant); }
.parts-stepper md-step::part(label) { text-transform: uppercase; letter-spacing: .08em; font-size: 11px; }
.parts-stepper md-step::part(description) { font-style: italic; }
</style>
<MdStepper className="parts-stepper" active="1" label="Styled parts" style={{ inlineSize: '100%' }}>
<MdStep label="Account" description="Signed in" completed></MdStep>
<MdStep label="Shipping" description="Where it goes"></MdStep>
<MdStep label="Payment" description="Card details"></MdStep>
<div slot="content">The nav row is right-aligned via ::part(nav).</div>
</MdStepper>
</>
);
}// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<style>
.parts-stepper::part(nav) { justify-content: flex-end; }
.parts-stepper::part(list) { padding-block-end: 8px; border-block-end: 1px solid var(--md-sys-color-outline-variant); }
.parts-stepper md-step::part(label) { text-transform: uppercase; letter-spacing: .08em; font-size: 11px; }
.parts-stepper md-step::part(description) { font-style: italic; }
</style>
<md-stepper class="parts-stepper" active="1" label="Styled parts" style="inline-size: 100%;">
<md-step label="Account" description="Signed in" completed></md-step>
<md-step label="Shipping" description="Where it goes"></md-step>
<md-step label="Payment" description="Card details"></md-step>
<div slot="content">The nav row is right-aligned via ::part(nav).</div>
</md-stepper><script setup>
import '@awc-ui/core/define';
</script>
<template>
<style>
.parts-stepper::part(nav) { justify-content: flex-end; }
.parts-stepper::part(list) { padding-block-end: 8px; border-block-end: 1px solid var(--md-sys-color-outline-variant); }
.parts-stepper md-step::part(label) { text-transform: uppercase; letter-spacing: .08em; font-size: 11px; }
.parts-stepper md-step::part(description) { font-style: italic; }
</style>
<md-stepper class="parts-stepper" active="1" label="Styled parts" style="inline-size: 100%;">
<md-step label="Account" description="Signed in" completed></md-step>
<md-step label="Shipping" description="Where it goes"></md-step>
<md-step label="Payment" description="Card details"></md-step>
<div slot="content">The nav row is right-aligned via ::part(nav).</div>
</md-stepper>
</template><script>
import '@awc-ui/core/define';
</script>
<style>
.parts-stepper::part(nav) { justify-content: flex-end; }
.parts-stepper::part(list) { padding-block-end: 8px; border-block-end: 1px solid var(--md-sys-color-outline-variant); }
.parts-stepper md-step::part(label) { text-transform: uppercase; letter-spacing: .08em; font-size: 11px; }
.parts-stepper md-step::part(description) { font-style: italic; }
</style>
<md-stepper class="parts-stepper" active="1" label="Styled parts" style="inline-size: 100%;">
<md-step label="Account" description="Signed in" completed></md-step>
<md-step label="Shipping" description="Where it goes"></md-step>
<md-step label="Payment" description="Card details"></md-step>
<div slot="content">The nav row is right-aligned via ::part(nav).</div>
</md-stepper>md-stepper::part(nav) { justify-content: flex-end;}md-step ·
md-tabs ·
md-progress-indicator ·
md-button
md-stepperTwo artefacts to give your AI agent so it generates correct UI with this component. The per-component spec answers "how do I use this exact tag?". The main-llm spec answers "which tag should I pick in the first place?".
md-stepper spec card
Identity · when to use / when NOT · decision cues · behavioural contract · do/don't · anti-patterns · full API. Paste into your agent when you're implementing with this component.
System-prompt preamble · decision matrix · token reference · page recipes · anti-patterns. Paste into the system prompt at the start of a piece of work.