Wizard
A controlled staged workflow shell with ordered step navigation, current content and an Action Bar. The host supplies stage content, validation, saving, availability and navigation. Completed stages show a check; current and upcoming stages show their number. Mark inaccessible future stages disabled. Optional hrefs preserve native navigation before JavaScript. The navigate event reports the requested value, previous value and trigger without changing value. The default slot contains the current stage; secondary and primary slots compose Action Bar for host-supplied Back, Continue or Finish actions. The disabled prop affects step navigation; the host disables its own content and actions when needed. Shared theme tokens provide palette-aware states and the step list wraps at narrow widths.
Staged workflow
The host marks completed and inaccessible stages, supplies the current stage content, and owns the Back and Continue actions.
Add details
Enter the details for this stage, then continue to review.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-wizard/ui-wizard.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-stack/ui-stack.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-text/ui-text.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-input/ui-input.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<ui-wizard
value="details"
label="Setup progress"
steps="[{"value":"start","label":"Start","complete":true},{"value":"details","label":"Details"},{"value":"review","label":"Review","disabled":true},{"value":"finish","label":"Finish","disabled":true}]"
>
<ui-stack
gap="s"
>
<ui-text
as="h2"
size="lg"
>Add details</ui-text>
<ui-text>Enter the details for this stage, then continue to review.</ui-text>
<ui-input
label="Display name"
value="Example workspace"
></ui-input>
</ui-stack>
<ui-button
slot="secondary"
>Back</ui-button>
<ui-button
slot="primary"
preset="primary"
>Continue</ui-button>
</ui-wizard><script setup>
import { UiButton, UiInput, UiStack, UiText, UiWizard } from "@threadlabs/looma";
</script>
<template>
<UiWizard
value="details"
label="Setup progress"
:steps="[{ value: 'start', label: 'Start', complete: true }, { value: 'details', label: 'Details' }, { value: 'review', label: 'Review', disabled: true }, { value: 'finish', label: 'Finish', disabled: true }]"
>
<UiStack
gap="s"
>
<UiText
as="h2"
size="lg"
>Add details</UiText>
<UiText>Enter the details for this stage, then continue to review.</UiText>
<UiInput
label="Display name"
value="Example workspace"
/>
</UiStack>
<template #secondary>
<UiButton>Back</UiButton>
</template>
<template #primary>
<UiButton
preset="primary"
>Continue</UiButton>
</template>
</UiWizard>
</template>import { UiButton, UiInput, UiStack, UiText, UiWizard } from "@threadlabs/looma";
export default function Example() {
return <UiWizard
value="details"
label="Setup progress"
steps={[{"value":"start","label":"Start","complete":true},{"value":"details","label":"Details"},{"value":"review","label":"Review","disabled":true},{"value":"finish","label":"Finish","disabled":true}]}
slots={{
secondary: <UiButton>Back</UiButton>,
primary: <UiButton
preset="primary"
>Continue</UiButton>,
}}
>
<UiStack
gap="s"
>
<UiText
as="h2"
size="lg"
>Add details</UiText>
<UiText>Enter the details for this stage, then continue to review.</UiText>
<UiInput
label="Display name"
value="Example workspace"
/>
</UiStack>
</UiWizard>;
}/** @jsxImportSource preact */
import { UiButton, UiInput, UiStack, UiText, UiWizard } from "@threadlabs/looma";
export default function Example() {
return <UiWizard
value="details"
label="Setup progress"
steps={[{"value":"start","label":"Start","complete":true},{"value":"details","label":"Details"},{"value":"review","label":"Review","disabled":true},{"value":"finish","label":"Finish","disabled":true}]}
slots={{
secondary: <UiButton>Back</UiButton>,
primary: <UiButton
preset="primary"
>Continue</UiButton>,
}}
>
<UiStack
gap="s"
>
<UiText
as="h2"
size="lg"
>Add details</UiText>
<UiText>Enter the details for this stage, then continue to review.</UiText>
<UiInput
label="Display name"
value="Example workspace"
/>
</UiStack>
</UiWizard>;
}<script>
import { UiButton, UiInput, UiStack, UiText, UiWizard } from "@threadlabs/looma";
</script>
{#snippet secondary()}
<UiButton>Back</UiButton>
{/snippet}
{#snippet primary()}
<UiButton
preset="primary"
>Continue</UiButton>
{/snippet}
<UiWizard
value="details"
label="Setup progress"
steps={[{"value":"start","label":"Start","complete":true},{"value":"details","label":"Details"},{"value":"review","label":"Review","disabled":true},{"value":"finish","label":"Finish","disabled":true}]}
slots={{ secondary, primary }}
>
<UiStack
gap="s"
>
<UiText
as="h2"
size="lg"
>Add details</UiText>
<UiText>Enter the details for this stage, then continue to review.</UiText>
<UiInput
label="Display name"
value="Example workspace"
/>
</UiStack>
</UiWizard>import { Component } from "@angular/core";
import { UiButton, UiInput, UiStack, UiText, UiWizard } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiWizard, UiStack, UiText, UiInput, UiButton],
template: `
<div
ui-wizard
[value]="'details'"
[label]="'Setup progress'"
[steps]="[{ value: 'start', label: 'Start', complete: true }, { value: 'details', label: 'Details' }, { value: 'review', label: 'Review', disabled: true }, { value: 'finish', label: 'Finish', disabled: true }]"
>
<div
ui-stack
[gap]="'s'"
>
<ng-template
ui-text
[as]="'h2'"
[size]="'lg'"
>Add details</ng-template>
<ng-template
ui-text
>Enter the details for this stage, then continue to review.</ng-template>
<input
ui-input
label="Display name"
[value]="'Example workspace'"
>
</div>
<ng-container ngProjectAs="[slot='secondary']">
<ng-template
ui-button
>Back</ng-template>
</ng-container>
<ng-container ngProjectAs="[slot='primary']">
<ng-template
ui-button
[preset]="'primary'"
>Continue</ng-template>
</ng-container>
</div>
`,
})
export default class Example {}Native destinations
Visited stages retain ordinary link navigation before JavaScript. Disabled future stages have no destination. Replace these example query destinations with your own routes.
Review your details before finishing.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-wizard/ui-wizard.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-text/ui-text.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<ui-wizard
value="review"
label="Review progress"
steps="[{"value":"details","label":"Details","complete":true,"href":"?step=details"},{"value":"review","label":"Review","href":"?step=review"},{"value":"finish","label":"Finish","disabled":true,"href":"?step=finish"}]"
>
<ui-text>Review your details before finishing.</ui-text>
<ui-button
slot="secondary"
href="?step=details"
>Back</ui-button>
<ui-button
slot="primary"
preset="primary"
>Finish</ui-button>
</ui-wizard><script setup>
import { UiButton, UiText, UiWizard } from "@threadlabs/looma";
</script>
<template>
<UiWizard
value="review"
label="Review progress"
:steps="[{ value: 'details', label: 'Details', complete: true, href: '?step=details' }, { value: 'review', label: 'Review', href: '?step=review' }, { value: 'finish', label: 'Finish', disabled: true, href: '?step=finish' }]"
>
<UiText>Review your details before finishing.</UiText>
<template #secondary>
<UiButton
href="?step=details"
>Back</UiButton>
</template>
<template #primary>
<UiButton
preset="primary"
>Finish</UiButton>
</template>
</UiWizard>
</template>import { UiButton, UiText, UiWizard } from "@threadlabs/looma";
export default function Example() {
return <UiWizard
value="review"
label="Review progress"
steps={[{"value":"details","label":"Details","complete":true,"href":"?step=details"},{"value":"review","label":"Review","href":"?step=review"},{"value":"finish","label":"Finish","disabled":true,"href":"?step=finish"}]}
slots={{
secondary: <UiButton
href="?step=details"
>Back</UiButton>,
primary: <UiButton
preset="primary"
>Finish</UiButton>,
}}
>
<UiText>Review your details before finishing.</UiText>
</UiWizard>;
}/** @jsxImportSource preact */
import { UiButton, UiText, UiWizard } from "@threadlabs/looma";
export default function Example() {
return <UiWizard
value="review"
label="Review progress"
steps={[{"value":"details","label":"Details","complete":true,"href":"?step=details"},{"value":"review","label":"Review","href":"?step=review"},{"value":"finish","label":"Finish","disabled":true,"href":"?step=finish"}]}
slots={{
secondary: <UiButton
href="?step=details"
>Back</UiButton>,
primary: <UiButton
preset="primary"
>Finish</UiButton>,
}}
>
<UiText>Review your details before finishing.</UiText>
</UiWizard>;
}<script>
import { UiButton, UiText, UiWizard } from "@threadlabs/looma";
</script>
{#snippet secondary()}
<UiButton
href="?step=details"
>Back</UiButton>
{/snippet}
{#snippet primary()}
<UiButton
preset="primary"
>Finish</UiButton>
{/snippet}
<UiWizard
value="review"
label="Review progress"
steps={[{"value":"details","label":"Details","complete":true,"href":"?step=details"},{"value":"review","label":"Review","href":"?step=review"},{"value":"finish","label":"Finish","disabled":true,"href":"?step=finish"}]}
slots={{ secondary, primary }}
>
<UiText>Review your details before finishing.</UiText>
</UiWizard>import { Component } from "@angular/core";
import { UiButton, UiText, UiWizard } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiWizard, UiText, UiButton],
template: `
<div
ui-wizard
[value]="'review'"
[label]="'Review progress'"
[steps]="[{ value: 'details', label: 'Details', complete: true, href: '?step=details' }, { value: 'review', label: 'Review', href: '?step=review' }, { value: 'finish', label: 'Finish', disabled: true, href: '?step=finish' }]"
>
<ng-template
ui-text
>Review your details before finishing.</ng-template>
<ng-container ngProjectAs="[slot='secondary']">
<ng-template
ui-button
[href]="'?step=details'"
>Back</ng-template>
</ng-container>
<ng-container ngProjectAs="[slot='primary']">
<ng-template
ui-button
[preset]="'primary'"
>Finish</ng-template>
</ng-container>
</div>
`,
})
export default class Example {}API
Package: @threadlabs/looma · Native root: div
Attributes
| Name | Description | Property | Type | Default | Allowed values |
|---|---|---|---|---|---|
disabled | Disables step navigation. The host controls availability of its slotted actions and content. | disabled | boolean | false | — |
label | Accessible name of the step navigation. | label | string | Progress | — |
steps | Stages in their reading order. Mark inaccessible stages disabled; availability and completion belong to the host. | steps | list(object)
| [] | — |
value | Current stage value. Consumer controlled; navigation reports intent without changing it. | value | string | | — |
Framework props
| Name | Type | Default | Allowed values |
|---|---|---|---|
disabled | boolean | false | — |
label | string | Progress | — |
steps | readonly ({ readonly value: string; readonly label: string; readonly complete?: boolean | null; readonly disabled?: boolean | null; readonly href?: string | null })[]
| [] | — |
value | string | | — |
Events
| Name | Detail | Description |
|---|---|---|
navigate | object
| Emitted with the declared detail payload. |
Slots
| Name | Purpose |
|---|---|
default | Default child content. |
secondary | Named content for the secondary region. |
primary | Named content for the primary region. |
Design tokens
Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.
Component tokens
No component tokens.
Shared tokens
| Token | Fallback |
|---|---|
--ui-accent-solid | — |
--ui-border-strong | — |
--ui-border-width | — |
--ui-font-size-sm | — |
--ui-line-height-tight | — |
--ui-on-accent | — |
--ui-selection-border | — |
--ui-selection-surface | — |
--ui-selection-text | — |
--ui-space-2 | — |
--ui-space-4 | — |
--ui-text-primary | — |
--ui-text-secondary | — |