Skip to content

feat(frontend): redesign onboarding wizard#5116

Open
NicholasKissel wants to merge 1 commit into
mainfrom
NicholasKissel/onboarding-ux-review
Open

feat(frontend): redesign onboarding wizard#5116
NicholasKissel wants to merge 1 commit into
mainfrom
NicholasKissel/onboarding-ux-review

Conversation

@NicholasKissel
Copy link
Copy Markdown
Member

@NicholasKissel NicholasKissel commented May 28, 2026

Redesigns the Rivet onboarding wizard (cloud/compute flavor) for clearer information architecture, a more cohesive look, and smoother step transitions.

Structure

  • Consistent step anatomy: every step now renders progress → title → one-line description → content → labeled action. Added a description slot to StepperForm (singlePage) so the intro line is structural instead of ad-hoc per step.
  • Framed the wizard in a card so it matches the create-project screen and the rest of the dashboard instead of floating on bare background.
  • Removed the dead "Explore Rivet Actors" carousel step.

Per-step

  • Install: reframed as skill + package — two required, numbered parts (not an either/or), using the existing StepNumber pattern. One consistent command treatment.
  • Build your first Actor: fixed the agent-prompt banner copy (it referenced "deploy to Rivet Compute" on a local-dev step); framed agent vs. quickstart as recommended + alternative.
  • Where to deploy: title/description normalized; Beta badge wired on provider cards.
  • Connect your backend: explicit "automatic (agent) / or set it up manually" framing so the agent banner and numbered steps stop competing; added a Serverless vs Runner explainer.
  • Verify deployment: replaced the dead-air "Waiting…" line with a status checklist (spinner/check rows) and fixed the CTA hierarchy (demoted "Skip to dashboard").

Animation

  • Smooth height transition: a measured AnimatedHeight wrapper animates the card between differently-sized steps instead of snapping.
  • Step transition is a cross-fade (a horizontal slide fought the height clip and got cut off at the edges).
  • Removed the now-dead direction machinery (useStepperDirection, custom props) and the unused isLastVisible prop on StepPanel.

Misc

  • Normalized provider logos to a consistent size-5 box (the Rivet mark's aspect ratio didn't match the others under the old fixed-width sizing).
  • Sentence-case copy, consistent spacing, labeled "Continue" buttons.

🤖 Generated with Claude Code

Copy link
Copy Markdown
Member Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@railway-app
Copy link
Copy Markdown

railway-app Bot commented May 28, 2026

🚅 Deployed to the rivet-pr-5116 environment in rivet-frontend

Service Status Web Updated (UTC)
frontend-cloud 😴 Sleeping (View Logs) Web May 29, 2026 at 8:24 pm
website 😴 Sleeping (View Logs) Web May 28, 2026 at 8:43 pm
ladle ✅ Success (View Logs) Web May 28, 2026 at 8:33 pm
kitchen-sink ❌ Build Failed (View Logs) Web May 28, 2026 at 8:32 pm
frontend-inspector ❌ Build Failed (View Logs) Web May 28, 2026 at 8:31 pm
mcp-hub ✅ Success (View Logs) Web May 28, 2026 at 8:31 pm

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 28, 2026

Code Review: feat(frontend): redesign onboarding wizard

Overview

This PR redesigns the onboarding wizard into a fixed-width card layout (~36rem), removes the "Explore Rivet Actors" step, improves animations by replacing directional sliding with a cross-fade + animated height approach, adds an OnboardingProgress bar, and refactors several inline sections into reusable components.


Bugs / Issues

1. Unhandled clipboard promise in CommandBox (getting-started.tsx)

navigator.clipboard.writeText returns a Promise that can reject (e.g. in non-secure or permission-denied contexts). The success toast fires unconditionally before the write resolves:

onClick={(e) => {
    navigator.clipboard.writeText(command); // rejection silently swallowed
    toast.success("Copied to clipboard");
}}

The existing AgentPromptBanner has the same pattern, but this is a new component that should fix it:

navigator.clipboard.writeText(command).then(
    () => toast.success("Copied to clipboard"),
    () => toast.error("Failed to copy"),
);

2. Misleading constant name: slideVariants (stepper-form.tsx, near the top of the new animation section)

The comment directly above it explains the animation is a cross-fade (opacity only), not a slide. The name contradicts the comment and will confuse future readers. Consider renaming to fadeVariants.


Minor Notes

3. Type cast on step.description (stepper-form.tsx)

(step as Step).description works but exposes a leaky type boundary. The local Step type augments the Stepperize type — if the library narrows its exported type in a future version, this cast silently breaks. A safer alternative:

{"description" in step && step.description ? ( ... ) : null}

4. Empty <span /> as layout spacer (getting-started.tsx)

) : (
    <span />
)}

This is used as a flex space-between placeholder when no deployment URL exists. The intent isn't obvious on first read. Consider a div with flex-1 or restructuring with justify-end for the right-side button.

5. aria-valuemin value

aria-valuemin={1} is consistent with the 1-indexed display, but WAI-ARIA examples typically show 0 as the minimum for progress indicators. Either is valid given the explicit aria-valuetext — flagging in case it is unintentional.


What Works Well

  • AnimatedHeight component is a clean, self-contained solution. The ResizeObserver with disconnect() cleanup is correct. The comment explaining why horizontal slide was dropped (clipped by the overflow container) is exactly the kind of non-obvious constraint that belongs in a comment.
  • OnboardingProgress has proper role="progressbar" with full aria attributes — good accessibility baseline.
  • containsSecret on AgentPromptBanner is a good security UX improvement; the inline warning copy is clear and actionable.
  • try/catch around mutateAsyncManagedPool — the previous code would let an unhandled rejection block the step or silently fail. Catching it, surfacing a recoverable toast, and continuing is the right behavior for an onboarding flow.
  • Extracted components (OrDivider, CommandBox, VerifyStatusRow) are each used in multiple places; the extraction is justified.
  • StepContent simplification (removing wide and the dynamic max-width animation) is a clean reduction now that the card has a fixed width.
  • Removing useStepperDirection and the directional slide logic removes a subtle ref-during-render pattern; the cross-fade is simpler and avoids potential React strict-mode issues.

PR Description

The description template was not filled out — no summary, no test plan, no checked boxes. For a UI change of this scope, please add a brief description of what changed and confirmation of how it was verified (e.g. screenshots, manual test in local dev).

@NicholasKissel NicholasKissel requested a review from jog1t May 28, 2026 21:40
Comment on lines +173 to +195
function AnimatedHeight({ children }: { children: ReactNode }) {
const innerRef = useRef<HTMLDivElement>(null);
const [height, setHeight] = useState<number | "auto">("auto");

useLayoutEffect(() => {
const el = innerRef.current;
if (!el) return;
const measure = () => setHeight(el.offsetHeight);
measure();
const observer = new ResizeObserver(measure);
observer.observe(el);
return () => observer.disconnect();
}, []);

return directionRef.current;
return (
<motion.div
animate={{ height }}
transition={{ duration: 0.25, ease: [0.4, 0, 0.2, 1] }}
style={{ overflow: "hidden" }}
>
<div ref={innerRef}>{children}</div>
</motion.div>
);
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.

i feel like this is supported in motion by a layout prop or height: auto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants