Spec version v0.2.0

Wizard

Purpose

The wizard composition handles multi-step creation and setup flows — processes where a user must complete a sequence of discrete, ordered steps to produce a new entity or complete a configuration task. Wizards are appropriate when the task involves five or more distinct input groups, when later steps depend on choices made in earlier ones, or when completing the entire task in a single flat form would overwhelm the user with too many fields at once.

Use a wizard for: creating a new server, project, or pipeline; onboarding a user through initial product setup; configuring a multi-stage integration; provisioning a resource with dependencies.

Do not use a wizard for a short form that fits comfortably in a standard modal (use the modal pattern with a form-layout instead). Do not use a wizard for an edit flow — wizards are for first-time creation or initial configuration, not for revisiting settings on an existing entity (use the settings-page composition for that). Do not use a wizard as a navigation metaphor for informational content with no form inputs.

Structure

The wizard is a large modal overlay, not a page. It sits above the application page using the standard modal stacking order. Its internal layout divides into two columns: a fixed-width sidebar on the left and a scrollable content area on the right.

+-----------------------------------------------------------------------+
|  [Overlay: --force-color-bg-overlay, z-index: modal-backdrop]        |
|  +-------------------------------------------------------------------+|
|  |  Sidebar (240px)       |  Content Area                           ||
|  |  bg: bg-muted          |  bg: bg-surface                         ||
|  |  border-right:         |                                         ||
|  |    border-default      |  Step Title (2xl, semibold)             ||
|  |                        |  Step description (sm, text-secondary)  ||
|  |  ● Step 1 (complete)   |                                         ||
|  |  ● Step 2 (complete)   |  +---------------------------------+    ||
|  |  ▶ Step 3 (active)     |  |  Form fields                    |    ||
|  |  ○ Step 4 (pending)    |  |  (form-layout pattern)          |    ||
|  |  ○ Step 5 (pending)    |  |                                 |    ||
|  |                        |  +---------------------------------+    ||
|  |                        |                                         ||
|  |                        |  [Footer: Back]      [Next / Create]    ||
|  +-------------------------------------------------------------------+|
+-----------------------------------------------------------------------+

Modal Container

The wizard panel is a modal. The full-viewport backdrop uses background: var(--force-color-bg-overlay) at z-index: var(--force-z-index-modal-backdrop). The panel itself uses background: var(--force-color-bg-surface), border-radius: var(--force-radius-modal) (12px), box-shadow: var(--force-shadow-xl), and z-index: var(--force-z-index-modal). The panel is centered in the viewport both vertically and horizontally and must not touch the viewport edges — maintain at least 40px clearance on all sides.

Three width sizes are available, each chosen at the point of implementation based on form complexity:

  • Medium (600px): simple wizards with three or four short steps and minimal fields per step. No sidebar; step indicators collapse to a horizontal step bar above the content.
  • Large (900px, default): standard multi-step creation flows. Full two-column layout with sidebar.
  • Extra-large (1200px): complex wizards with dense form steps, side-by-side field columns within a step, or embedded previews.

The medium size omits the sidebar column entirely and replaces it with a horizontal stepped progress indicator above the content area. The large and extra-large sizes use the full two-column layout described below.

Sidebar

The sidebar is 240px wide, fixed, and non-scrollable. Its background is var(--force-color-bg-muted) and it is separated from the content area by border-right: 1px solid var(--force-color-border-default). The sidebar has border-radius: var(--force-radius-modal) only on its top-left and bottom-left corners — it shares the modal's overall rounded corners. Internal padding is var(--force-spacing-6) (24px) on all sides.

The sidebar contains the wizard title at the top and the step list below it. The wizard title (for example, "Create Repository" or "New Pipeline Setup") is set in --force-font-size-base (16px), --force-font-weight-semibold (600), --force-color-text-primary, with margin-bottom: var(--force-spacing-6). Do not repeat the wizard title in the content area header — the step title in the content area is sufficient orientation.

The step list is a vertical stack of step items spaced var(--force-spacing-2) (8px) apart. Each step item has three visible sub-elements: a state indicator icon, a step label, and an optional brief sub-label. Step labels use --force-font-size-sm (14px), --force-font-weight-medium (500). Sub-labels, when present, use --force-font-size-xs (12px), --force-color-text-tertiary.

Step items are not interactive — the user cannot click a step in the sidebar to jump to it. Steps are navigated only by the Back and Next buttons. This is intentional: wizard steps may have dependencies, and skipping validation by clicking ahead would break data integrity.

Step States

Each step in the sidebar displays one of three states, communicated through both color and icon (never color alone):

Completed: A filled checkmark icon in --force-color-icon-success (green-500), 16px. The step label uses --force-color-text-secondary. These steps are fully opaque — they are not dimmed.

Active (current): A filled circle or right-pointing chevron icon in --force-color-bg-primary (indigo-500), 16px. The step label uses --force-color-text-interactive-active (indigo-700), --force-font-weight-semibold (600). The step item row has background: var(--force-color-bg-interactive-active), border-radius: var(--force-radius-md) (6px), and a 3px left border in --force-color-border-primary (indigo-500), following the same active-item convention used in the settings-page category nav.

Pending (not yet reached): An unfilled circle icon in --force-color-icon-default (neutral-600), 16px. The step label uses --force-color-text-disabled. The entire step item is rendered at opacity: var(--force-opacity-subtle) (0.7) to visually distinguish pending steps from completed and active ones without removing them from view.

Content Area

The content area fills all remaining width to the right of the sidebar. Its background is var(--force-color-bg-surface). Internal padding is var(--force-spacing-6) (24px) on all sides with an additional padding-bottom: var(--force-spacing-20) (80px) to ensure that content never slides underneath the fixed footer navigation. The content area is vertically scrollable when a step's form fields overflow the visible panel height; the sidebar and footer remain fixed.

At the top of the content area, the active step title is displayed in --force-font-size-2xl (24px), --force-font-weight-semibold (600), --force-color-text-primary, with margin-bottom: var(--force-spacing-2) (8px). Immediately below the title, an optional one-to-two sentence step description uses --force-font-size-sm (14px), --force-color-text-secondary, margin-bottom: var(--force-spacing-6) (24px). Below the description, form fields follow the form-layout pattern: a vertical stack with gap: var(--force-spacing-4) (16px) between field groups.

Footer Navigation

A sticky footer is anchored to the bottom of the content area (not the sidebar). It has border-top: 1px solid var(--force-color-border-default), background: var(--force-color-bg-surface), and padding: var(--force-spacing-4) var(--force-spacing-6) (16px 24px). It contains two buttons laid out with justify-content: space-between so the Back button sits on the left and the forward action sits on the right.

The Back button uses the secondary button variant. On the first step, the Back button is replaced by a Cancel button also using the secondary variant. Clicking Cancel triggers the unsaved changes confirmation dialog (see Guidance section below) if any field on any visited step has been modified.

The forward action button uses the primary button variant. Its label is "Next" for all steps except the final step, where it changes to "Create", "Submit", or a domain-appropriate verb that describes the result of completing the wizard. Do not use "Finish" or "Done" — these are vague. Use the specific entity-creation verb that matches the wizard's purpose.

If the current step has validation errors, the Next or Create button must be in a disabled state using opacity: var(--force-opacity-disabled) (0.5) applied to the button element, cursor: not-allowed, and pointer-events: none. The button label does not change — only its interactivity is removed. Inline field validation errors are the primary signal; the disabled Next button is a secondary guard.

Responsive Behavior

At and above --force-breakpoint-lg (992px), the full two-column wizard layout is displayed. The modal panel centers in the viewport with a maximum height of calc(100vh - 80px), allowing scroll within the content area. The sidebar does not scroll.

Between --force-breakpoint-md (768px) and --force-breakpoint-lg (992px), large and xl wizards reduce to the medium layout: the sidebar collapses and is replaced by a compact horizontal step bar at the top of the content area. The step bar shows the step number and label for the active step and uses dot indicators for completed and pending steps. The modal panel width fills calc(100vw - 48px).

Below --force-breakpoint-md (768px), the wizard panel takes width: calc(100vw - 32px), min-height: 80vh, and anchors to the bottom of the viewport like a bottom sheet. Step indicators are reduced to a simple "Step X of Y" text counter in --force-font-size-xs (12px), --force-color-text-tertiary, above the step title. Footer buttons stack vertically with the primary action on top and Back/Cancel below, each taking full width.

Token Usage

Element Token
Overlay backdrop background --force-color-bg-overlay
Overlay backdrop z-index --force-z-index-modal-backdrop
Panel background --force-color-bg-surface
Panel border radius --force-radius-modal (12px)
Panel shadow --force-shadow-xl
Panel z-index --force-z-index-modal
Sidebar background --force-color-bg-muted
Sidebar width 240px (not tokenized; use as a documented constant)
Sidebar separator --force-color-border-default (1px right border)
Sidebar padding --force-spacing-6 (24px)
Wizard title size --force-font-size-base (16px)
Wizard title weight --force-font-weight-semibold (600)
Wizard title bottom margin --force-spacing-6 (24px)
Step item gap --force-spacing-2 (8px)
Step label size --force-font-size-sm (14px)
Step label weight (default) --force-font-weight-medium (500)
Step sub-label size --force-font-size-xs (12px)
Step sub-label color --force-color-text-tertiary
Active step background --force-color-bg-interactive-active
Active step border radius --force-radius-md (6px)
Active step left border --force-color-border-primary (3px)
Active step label color --force-color-text-interactive-active
Active step label weight --force-font-weight-semibold (600)
Active step icon color --force-color-bg-primary (indigo-500)
Completed step icon color --force-color-icon-success
Completed step label color --force-color-text-secondary
Pending step opacity --force-opacity-subtle (0.7)
Pending step label color --force-color-text-disabled
Content area padding --force-spacing-6 (24px)
Step title size --force-font-size-2xl (24px)
Step title weight --force-font-weight-semibold (600)
Step title bottom margin --force-spacing-2 (8px)
Step description size --force-font-size-sm (14px)
Step description color --force-color-text-secondary
Step description bottom margin --force-spacing-6 (24px)
Form field vertical gap --force-spacing-4 (16px)
Footer border --force-color-border-default (1px top border)
Footer background --force-color-bg-surface
Footer padding --force-spacing-4 vertical, --force-spacing-6 horizontal
Back / Cancel button variant secondary
Next / Create button variant primary
Disabled Next button opacity --force-opacity-disabled (0.5)
Input border (default) --force-color-border-default
Input border (error) --force-color-border-error
Input focus ring --force-shadow-focus-ring + --force-color-border-focus
Error focus ring --force-shadow-focus-ring-error + --force-color-border-error
Error message text color --force-color-text-error
Error message text size --force-font-size-xs (12px)
Wizard entrance animation duration --force-duration-normal (250ms)
Wizard entrance easing --force-easing-decelerate
Wizard exit animation duration --force-duration-fast (150ms)
Wizard exit easing --force-easing-accelerate
Step content transition duration --force-duration-normal (250ms)
Step content transition easing --force-easing-standard

Accessibility

The wizard modal follows all requirements of the ARIA dialog pattern. The panel element carries role="dialog", aria-modal="true", and aria-labelledby pointing to the wizard title element in the sidebar (not the step title). The wizard title element receives a stable id that does not change as steps advance.

When the wizard opens, focus moves to the first focusable element inside the content area — typically the first form field of step 1, or the step title heading if there are no immediate form fields. When the wizard closes (whether by completion, cancel, or escape), focus returns to the trigger element that opened it.

The sidebar step list is a <ol> element (ordered list) because steps have a meaningful sequence. Each <li> item carries an aria-current="step" attribute on the active item. Completed items carry aria-disabled="false" and include a visually hidden text suffix " — completed" appended to the step label for screen reader users. Pending items carry a visually hidden text suffix " — not yet available". Do not use aria-disabled="true" on pending steps — they are informational, not interactive controls.

Body scroll must be locked while the wizard is open. Assign overflow: hidden to <body> and restore it on close.

The Escape key must close the wizard. If any step has been touched (any field modified on any visited step), pressing Escape must trigger the unsaved changes confirmation dialog before closing. The confirmation dialog itself must also trap focus and be dismissible with Escape, at which point focus returns to the wizard.

Step transitions must be announced to screen readers. When the user advances to a new step, use an aria-live="polite" region — or move focus to the new step title heading — so that screen reader users hear the new step context. Do not rely on visual motion alone. Moving focus to the step title heading (<h2> or equivalent) is the preferred approach because it both announces the new step name and provides a logical keyboard entry point for the content that follows.

Form validation within the wizard follows the same rules as any other Force UI form: each field in error carries aria-invalid="true" and aria-describedby pointing to its error message element. Error messages use role="alert" or are surfaced through an aria-live="assertive" region so they are read immediately when they appear. When the user attempts to advance with validation errors, focus must move to the first field in error state on the current step.

All icon indicators in the sidebar (checkmark, active indicator, circle) must have their meaning conveyed through visually hidden text, since icons alone are not perceivable by screen reader users. For example, the completed checkmark icon's container includes a visually hidden <span> reading "Completed:".

Guidance

Choose the wizard size before building. The large (900px) size is the default and covers the majority of creation flows in Perforce products. Use medium (600px) only when there are three or fewer steps with fewer than five fields each. Use extra-large (1200px) only when a step requires side-by-side input columns, an inline preview pane, or a complex table-like configuration grid. Using xl for a simple three-field form is excessive and makes the overlay feel poorly proportioned against the form.

Keep steps focused. Each step should address a single logical concern. A step title like "General Settings" that covers eight unrelated fields is not a focused step — it is a settings form wearing a wizard costume. If a step cannot be described in three words or fewer, it is probably doing too much.

Do not re-validate every field on every step transition. Validate only the fields on the current step when the user clicks Next. Fields on previously completed steps have already been validated. Do not re-run prior-step validation unless returning to a completed step via the Back button, in which case the step is re-editable and its current values are validated again only if the user clicks Next again.

Do not use the wizard for a destructive process. A wizard that ends in deleting or revoking data is wrong — destructive actions belong in confirmation dialogs, not multi-step creation flows. A wizard's final action must create or configure something.

Preserve entered data when the user navigates Back. When the user returns to a previous step, all field values entered during the original visit must be pre-populated. Clearing previously entered values on Back navigation is a critical UX defect.

The confirmation dialog for unsaved changes must use the standard modal pattern (medium width, 600px), not a browser-native confirm(). It must present two choices: "Discard changes" (danger button variant) and "Continue editing" (secondary button variant). "Continue editing" is the safer action and receives focus by default when the dialog opens.

Do not allow the wizard to be closed by clicking the backdrop scrim. The scrim is not an interactive dismiss target for wizards because wizards contain significant in-progress user input. The only close affordances are the Cancel/Back button on the first step and the Escape key (both of which trigger the confirmation dialog if data is present). This differs from simple modals, where clicking the backdrop is a valid dismiss gesture.

Do not put step-spanning summary or review information inside the sidebar. The sidebar is a navigation landmark only — it shows step names and states, nothing more. If a review step is needed (for example, "Review and confirm" as the final step), it lives entirely in the content area.

Do not use more than eight steps. If a wizard requires more than eight steps, the task should be decomposed into separate flows or the content should be reorganized into fewer, broader steps. A progress indicator with more than eight items is visually unmanageable and signals an architectural problem, not a UI problem.

When the wizard's final action (Create, Submit, etc.) is triggered, the button must enter a loading state immediately — replace the label with a spinner and disable the button — to prevent double-submission. The sidebar step indicators do not animate during submission. On success, dismiss the wizard and show a toast notification confirming the action. On failure, return the button to its default state and display an inline error alert at the top of the content area using the standard alert/callout pattern with --force-color-bg-error-subtle, --force-color-border-error, and --force-color-text-error.

Composition

The wizard composes several existing Force UI patterns and components.

The outer structure follows the modal pattern exactly: a full-viewport fixed backdrop at --force-z-index-modal-backdrop using --force-color-bg-overlay, and a centered panel at --force-z-index-modal using --force-color-bg-surface, --force-radius-modal, and --force-shadow-xl. There is no additional wrapping card or container inside the modal panel.

The sidebar is not an application sidebar — it does not use the SidebarProvider or any sidebar component. It is a plain vertical list following the same token conventions as the settings-page category nav: step items use --force-color-bg-interactive-active and a 3px --force-color-border-primary left border for the active state, --force-color-text-interactive-active for the active label, and --force-color-text-secondary for completed labels. However, unlike the settings-page category nav, sidebar items are not clickable links.

Form fields within each step use the standard form-layout pattern: labeled inputs, selects, textareas, and toggles, each following the form-input pattern (height: 40px, border: 1px solid var(--force-color-border-default), border-radius: var(--force-radius-input), font-size: var(--force-font-size-sm)). Multi-column form layouts within a step use CSS Grid with gap: var(--force-spacing-4) (16px).

The footer uses Button components directly: secondary variant for Back/Cancel and primary variant for Next/Create. Do not put any other controls in the wizard footer — help links, secondary actions, and informational text belong in the content area, not the footer.

The unsaved-changes confirmation is a standard medium modal (600px) composed using the same modal pattern, rendered at the same --force-z-index-modal layer stacked on top of the wizard. The confirmation modal's backdrop is omitted because the wizard's own modal surface provides sufficient visual containment — adding a second full-viewport backdrop behind the confirmation dialog would double-darken the screen.

For the medium (600px) wizard without a sidebar, step progression is shown via a horizontal stepper component above the content area: a row of numbered circles connected by lines, using --force-color-bg-primary for the active step indicator, --force-color-icon-success for completed step indicators, and --force-color-border-default for the connector lines between steps.