Skip to content

feat(templates): add the vertical wizard page template - #5672

Merged
ernestt merged 6 commits into
mainfrom
template-form-wizard-vertical
Sep 5, 2026
Merged

feat(templates): add the vertical wizard page template#5672
ernestt merged 6 commits into
mainfrom
template-form-wizard-vertical

Conversation

@ernestt

@ernestt ernestt commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Fifth of the wizard family. Stacked on #5659 for the Stepper's one-line label truncation; siblings #5660 and #5664 sit alongside it.

What differentiates it

The steps live in a rail beside the form rather than above it. That suits steps that are long or unequal in size: the rail stays legible at five entries carrying descriptions, and the form beside it can run past a screenful without the progress scrolling away.

Wide

The rail's counterpart is a guidance panel on the other side, holding a summary of the current step until a field takes focus, then swapping to what that field affects:

Field guidance

It is deliberately not a live region. The panel changes on every focus move, and announcing it each time would talk over the field's own label and description on every tab stop. It is a labelled landmark instead, so a screen reader user can reach it on demand — which is also why the text says what a field affects rather than repeating the hint already attached to the field.

Giving up space in stages

Guidance goes first, at 1320px, since it is the only panel whose content is also reachable from the fields themselves:

Medium

The rail goes at 1000px, rotating into a horizontal stepper above the form so progress survives the loss of the column:

Narrow

The last step reviews everything as an icon-led metadata list:

Review

A blocked Continue sends focus to the first field needing attention, the same behaviour as #5664, so the footer never carries a second copy of a message the field is already showing:

Validation

Audit

92/100, grade A, under template rubric 1.3.

Category Score
Component purity 25/30
Icon purity 15/15
Custom CSS 12/15
Layout & structure 15/15
Doc metadata 10/10
Image handling 5/5
Code quality 10/10

The first audit came in at 85 on ten custom declarations, and the cover image was carrying most of them. Three rounds of that turned out to be the system's own job:

  • The cover framing was width, aspectRatio, objectFit, borderRadius and border written by hand. Card takes a width and draws the border and radius — and clips to it — while AspectRatio takes the ratio and the object fit. Five declarations to zero, and the radius now comes from the token rather than being named.
  • The 640px measure was width: 100% / maxWidth / marginInline: auto, which is character-for-character what Layout's contentWidth emits. That prop applies them to the middle row, which here holds the rail and the guidance panel as well as the form, so it would cap all three columns together instead of the one that should be capped. Center plus Stack's maxWidth says the same thing about the right box. Three to zero.

Three declarations remain, each without a prop to use instead: a zero flex basis so the dropzone column can shrink, a height so the dropzone ends level with the cover (FileInput exposes width but not height), and the footer lean described below.

The 5 points still on purity are the single <img> — the rubric's own listed exception, since there is no Image component and the src is a checked-in /template-assets/ path.

A defect the refactor exposed

Reworking the measure made it visible that the footer spans the page and centred on the page, which only matches the form when the panels are symmetric. With both up or both down it was fine, but in the middle tier — rail up, guidance gone — the buttons sat 142px left of the fields they submit. That was there before this refactor; it just was not obvious until the two boxes were expressed the same way.

The footer now leans with the column. Measured footer-to-form centre offset is 0px at 1440, 1360, 1319, 1200, 1100, 1010, 999, 900, 760 and 520 — both sides of both breakpoints.

Three other declarations came out during the first audit rather than being defended: position: sticky on the guidance panel was a no-op, confirmed by measuring (the form scrolls 478px while the panel heading holds at 81px, because the panel is its own layout cell and never scrolls), and minWidth: 0 duplicated a reset StackItem already applies. template.doc.mjs also had a stale sentence claiming the rail collapses to a counter, which the staged responsive work had replaced.

Test plan

  • All five steps render; validation gates each advance
  • Blocked Continue moves focus to the first invalid field (verified: lands on "Book title")
  • Guidance swaps to field help on focus and back to the step summary on blur
  • Responsive tiers verified at 1440 / 1150 / 760px — vertical+guidance, vertical, horizontal
  • Footer aligns with the form measure at 10 widths spanning both breakpoints, 0px offset
  • Dropzone ends level with the cover image
  • Zero console errors at every width tested
  • ESLint clean, no dead code
  • tsc clean against the docsite project

One follow-up commit early on: the guidance heading carried type="label", which is not a HeadingType — the prop only holds the display scale, so it sizes a heading up. The lookup missed, no size style applied, and it inherited whatever ambient size it sat next to. It looked right by accident and only surfaced in the docsite type check, since Vite's dev server does not typecheck. Now level={5} accessibilityLevel={2}: label-sized, level 2 in the outline.

Made with Cursor

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Aug 28, 2026
@vercel

vercel Bot commented Aug 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
astryx Ready Ready Preview Sep 5, 2026 5:03pm UTC

Request Review

@xinzi-bot xinzi-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks—the responsive rail and guidance hierarchy are thoughtful. Two interaction gaps remain: leaving a broken step backward can hide it from later validation, and errors for Categories, Territories, or Agreements have no focus target, so blocked Continue leaves keyboard users at the footer. Please make every navigation path preserve validation and move focus to each failed field.

[Automated review]

@cixzhang cixzhang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

xinzi-bot already caught the hidden-step and missing-focus paths; this confirms both and adds completion and responsive-host failures. This flow still isn’t safe to copy yet. People can bypass an invalid step, reach Submit, and get no completion; Cancel, Save and exit, and Enter are inert. When Categories, Territories, or Agreements are the only errors, Continue leaves focus on itself, so keyboard users must hunt for the field.

The three-column layout keys to viewport width. In a 700px host inside a wide page, it keeps the rail and guidance panel and crushes the form to 100px.

Could we make every exit path real, validate the whole wizard before completion, focus the first invalid field, and switch modes from available width? The final review should use provider-aware date and price formatting and the Wizard category so typechecking passes.

[Reviewed by Robohands]

@ernestt

ernestt commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Release-process check: this PR adds a consumer-visible template under the publishable @astryxdesign/cli 0.5.x package, but no .changeset/*.md file is present. It needs a patch entry whose first body line is a [feat] user-facing summary ending in (#5672) and whose second body line is @ernestt.

ernestt added a commit that referenced this pull request Sep 3, 2026
@ernestt
ernestt dismissed cixzhang’s stale review September 5, 2026 04:30

This is not a functional app. This is just a template to demonstrate layout

ernestt and others added 4 commits September 4, 2026 21:36
A five-step audiobook publishing flow. The differentiator against the other
wizards in the family is that the steps live in a rail beside the form rather
than above it, which suits steps that are long or unequal in size: the rail
stays legible at five entries with descriptions, and the form beside it can run
past a screenful without the progress scrolling away.

The rail's counterpart is a guidance panel on the other side. It carries a
summary of the current step until a field takes focus, then swaps to what that
field affects. Deliberately not a live region — the panel changes on every
focus move, and announcing it each time would talk over the field's own label
on every tab stop. It is a labelled landmark instead, so it can be reached on
demand, and its text says what a field affects rather than repeating the hint
already attached to the field.

Space is given up in stages rather than all at once. Guidance is the first to
go, since it is the only panel whose content is also reachable from the fields
themselves. The rail goes next, rotating into a horizontal stepper above the
form so progress survives the loss of the column.

A blocked Continue sends focus to the first field that needs attention, the
same behaviour as the horizontal wizard, so the footer never carries a second
copy of a message the field is already showing.

Co-authored-by: Cursor <cursoragent@cursor.com>
…type

`type="label"` is not a HeadingType — the prop only carries the display scale,
which sizes a heading up rather than down. The lookup missed, so no size style
was applied at all and the heading inherited whatever ambient size it landed
next to. It looked right by accident and broke the docsite type check.

Level 5 with accessibilityLevel 2 says it properly: label-sized, but level 2 in
the outline, because the panel sits beside the step rather than under it.

Co-authored-by: Cursor <cursoragent@cursor.com>
The cover was five hand-written declarations — width, aspect ratio, object fit,
radius, border — for an image the system already has components for. Card takes
a width and draws the border and radius, and clips to it; AspectRatio takes the
ratio and the object fit. Both now come from props, and the swap also picks up
the radius token rather than naming it.

The 640px measure was another three, and they were a verbatim copy of what
Layout's own contentWidth emits. That prop applies them to the middle row,
which here holds the rail and the guidance panel as well as the form, so it
would cap all three columns together. Center plus Stack's maxWidth says the
same thing about the one box that should be capped.

That left a real defect visible. The footer spans the page and centred on it,
which only matches the form when the panels are symmetric. In the middle tier —
rail up, guidance gone — the buttons sat 142px left of the fields they submit.
They now lean with the column, and the measured offset is 0 at every width
across both breakpoints.

Cover art swapped to illustrative-vertical-1.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ernestt
ernestt force-pushed the template-form-wizard-vertical branch from 6863053 to 45124a7 Compare September 5, 2026 04:36
@ernestt
ernestt changed the base branch from core-stepper-narrow-collapse to main September 5, 2026 04:36
@ernestt
ernestt enabled auto-merge (squash) September 5, 2026 04:52
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

PR Analysis Report

Current run: The current CI run concluded failure. Current analysis and preview links are unavailable.


Generated by PR Enrichment workflow | View current CI run

The template declares category 'Form - Wizard Vertical', which was missing
from the TemplateCategory union, failing the template-docs typecheck.
@ernestt
ernestt merged commit 5aca971 into main Sep 5, 2026
26 of 27 checks passed
@github-actions
github-actions Bot deleted the template-form-wizard-vertical branch September 6, 2026 06:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants