---
phase: 31
slug: puter.js-zero-config-cloud
status: draft
shadcn_initialized: true
preset: new-york / neutral / cssVariables
created: 2026-04-02
---

# Phase 31 — UI Design Contract

> Visual and interaction contract for Phase 31: Puter.js Zero-Config Cloud.
> Generated by gsd-ui-researcher. Verified by gsd-ui-checker.

---

## Design System

| Property | Value |
|----------|-------|
| Tool | shadcn |
| Preset | new-york, baseColor: neutral, cssVariables: true, style: new-york |
| Component library | Radix UI (via shadcn) |
| Icon library | lucide-react |
| Font | System UI (inherited from body; no custom font declared in index.css) |

Source: `/opt/nexus/ui/components.json` + `/opt/nexus/ui/src/index.css`

---

## Spacing Scale

Declared values (must be multiples of 4):

| Token | Value | Usage |
|-------|-------|-------|
| xs | 4px | Icon gaps (`gap-1`), inline badge padding |
| sm | 8px | Compact element spacing (`gap-2`), within-form row gaps |
| md | 16px | Default element spacing (`gap-4`), form field gaps |
| lg | 24px | Section padding (`p-6`), card interior padding |
| xl | 32px | Layout gaps (`p-8`) — matches existing wizard card `p-8` |
| 2xl | 48px | Major section breaks — not used in wizard context |
| 3xl | 64px | Page-level spacing — not used in wizard context |

Exceptions:
- Touch targets: `min-height: 44px` enforced globally via `@media (pointer: coarse)` in `index.css` — no per-component override required.
- Provider option cards: `p-4` (16px) interior padding, matching `ModeSelector` card pattern.
- Warning callout (Google OAuth risk): `px-3 py-2` (12px/8px) — compact inline alert pattern matching existing error state style.

Source: existing `NexusOnboardingWizard.tsx` uses `p-8`, `gap-6`, `gap-4`, `gap-2`. ModeSelector uses `p-4`, `gap-3`, `gap-1`.

---

## Typography

| Role | Size | Weight | Line Height |
|------|------|--------|-------------|
| Body | 14px (0.875rem) | 400 | 1.5 |
| Heading | 24px (1.5rem / text-2xl) | 600 | 1.2 |
| Caption / muted | 12px (0.75rem / text-xs) | 400 | 1.4 |

Notes:
- Heading size matches existing wizard `h1` (`text-2xl font-semibold`) — locked by Phase 30 component pattern.
- Label size matches existing form label pattern (`text-sm font-medium`).
- Body size matches existing paragraph + description pattern (`text-sm`).
- Caption is used for step indicator (`text-xs text-muted-foreground`), provider descriptions, and warning footnotes.
- No display-size text (>24px) is needed in this phase — the wizard card is constrained to `max-w-md`.

Source: `NexusOnboardingWizard.tsx` lines 214, 245, 281–283, 294.

---

## Color

All values are shadcn CSS custom properties resolved from `/opt/nexus/ui/src/index.css`.
Both light (Catppuccin Latte) and dark (Catppuccin Mocha) themes are supported.

| Role | Light value | Dark value | Usage |
|------|-------------|------------|-------|
| Dominant (60%) | `--background` (#eff1f5) | `--background` (#1e1e2e) | Wizard backdrop, page background |
| Secondary (30%) | `--card` (#e6e9ef) | `--card` (#181825) | Wizard card surface, provider option cards |
| Accent (10%) | `--primary` (#1e66f5) | `--primary` (#89b4fa) | Selected provider card border + tint, primary CTA button |
| Destructive | `--destructive` (#d20f39) | `--destructive` (#f38ba8) | Google OAuth policy-risk warning text; error state text |

Accent reserved for:
1. The currently selected provider option card border (`border-primary`) and tint (`bg-primary/5`)
2. The primary CTA button ("Continue with Puter", "Continue", "Get Started")
3. The focus ring on interactive inputs and buttons (`--ring`)

Do NOT use accent on: provider logos, status badges, neutral labels, section headings.

Source: `index.css` CSS custom properties; `ModeSelector.tsx` selected-state pattern (`border-primary bg-primary/5`).

---

## Component Inventory

New components introduced in this phase:

| Component | File | Reuses |
|-----------|------|--------|
| `ProviderSelectionStep` | `ui/src/components/onboarding/ProviderSelectionStep.tsx` | `Button`, `cn`, NexusOnboardingWizard card patterns |
| `PuterAuthButton` | `ui/src/components/onboarding/PuterAuthButton.tsx` | `Button` (variant default), lucide `LogIn` icon |
| `GoogleOAuthButton` | `ui/src/components/onboarding/GoogleOAuthButton.tsx` | `Button` (variant outline), lucide `AlertTriangle` icon for warning |
| `ApiKeyEntryForm` | `ui/src/components/onboarding/ApiKeyEntryForm.tsx` | `Input`, `Label`, `Button` |
| `ProviderRiskWarning` | inline in `GoogleOAuthButton` | styled `div` with `text-destructive` + `bg-destructive/10` — matches existing error block |

Existing components consumed without modification:

| Component | Source |
|-----------|--------|
| `Button` | `@/components/ui/button` |
| `Input` | `@/components/ui/input` |
| `Label` | `@/components/ui/label` |
| `Skeleton` | `@/components/ui/skeleton` |
| `NexusOnboardingWizard` | `@/components/NexusOnboardingWizard` — extended with new Step 4 |

---

## Interaction Contract

### Wizard Step Extension

The existing 3-step wizard becomes a 4-step wizard for users without a detected local adapter:

```
Step 1: Hardware Detection  →  Step 2: Mode Selection  →  Step 3: Provider Selection (NEW)  →  Step 4: Root Directory
```

For users with a detected local adapter (hermes_local or claude_local), Step 3 is shown but pre-populated with the detected provider highlighted and a "Continue" shortcut.

Step indicator text: "Step {N} of 4" (updated from current "Step {N} of 3").

### Provider Selection Step Layout

Three option cards in a vertical stack (matching `ModeSelector` grid pattern):

1. **Puter (free, zero-config)** — primary recommendation when no local AI and no API key
   - Card border: `border-primary bg-primary/5` when selected
   - Action: "Continue with Puter" button appears below cards when Puter is selected
   - Auth state: button shows spinner during `puter.auth.signIn()` popup
   - Post-auth state: button label changes to "Puter connected — Continue", icon: `CheckCircle` (lucide), border stays `border-primary`

2. **Google (Gemini free tier)** — shown with risk callout
   - Risk callout: amber/destructive inline alert (see Copywriting section) rendered inside the card when this option is selected — always visible before the "Sign in with Google" button is enabled
   - Action: "Sign in with Google" (disabled until risk callout has been displayed for 3 seconds — anti-accidental-click gate)
   - Post-auth state: "Google connected — Continue"

3. **API Key (subscription providers)** — manual entry
   - When selected: inline form expands below card (no new page) with provider select dropdown + API key input
   - Supported providers listed in dropdown: OpenAI, Anthropic, Groq (extensible)

4. **Skip for now** — ghost button below the three cards, always visible
   - Does not require a selection; proceeds to Step 4 with the probed default adapter

### Loading States

| State | Treatment |
|-------|-----------|
| Puter auth popup in progress | Button disabled, spinner icon (h-4 w-4 animate-spin), label "Connecting to Puter…" |
| Google OAuth in progress | Button disabled, spinner, label "Signing in…" |
| API key saving | Submit button disabled, spinner, label "Saving…" |
| Adapter probe in progress | Provider cards render with `Skeleton` placeholders where pre-detected tool badges would appear; probe is fire-and-forget (cards still interactive) |

### Error States

All errors render inline below the triggering element using the existing error block pattern:
```
<p className="text-sm text-destructive bg-destructive/10 rounded-md px-3 py-2">
  {errorMessage}
</p>
```

### Keyboard and Accessibility

- All provider option cards are `<button type="button">` elements (matching ModeSelector pattern) — keyboard navigable, focus ring via `--ring`.
- Risk warning callout uses `role="alert"` so screen readers announce it immediately when the Google option is selected.
- Auth buttons have `aria-busy="true"` during loading states.
- Skip button has accessible label: "Skip provider setup for now".

---

## Copywriting Contract

| Element | Copy |
|---------|------|
| Step heading (provider step) | "Choose a provider" |
| Step subheading | "No API keys needed for the zero-config path." |
| Primary CTA — Puter | "Continue with Puter" |
| Primary CTA — Google | "Sign in with Google" |
| Primary CTA — API key | "Save API key" |
| Primary CTA — generic Continue | "Continue" |
| Skip action | "Skip for now" |
| Puter card label | "Puter — free, zero-config" |
| Puter card description | "Free AI powered by your Puter.com account. No API key needed." |
| Puter cost attribution note | "Usage is billed to your Puter account, not to Nexus." |
| Google card label | "Google — Gemini free tier" |
| Google card description | "Sign in with Google to access Gemini via your Google account." |
| Google risk warning heading | "Policy risk — read before continuing" |
| Google risk warning body | "Google has suspended accounts that used third-party apps with Gemini credentials. This may affect your Gmail and Workspace access. Use a Google AI Studio API key instead if you want to avoid this risk." |
| Google safe alternative hint | "Safer option: enter a Gemini API key from Google AI Studio below." |
| API key card label | "API key — subscription provider" |
| API key card description | "Use your own OpenAI, Anthropic, or Groq API key." |
| Puter connected success | "Puter connected — Continue" |
| Google connected success | "Google connected — Continue" |
| Empty state (no provider selected, user tries to continue without skipping) | Not applicable — "Skip for now" is always available; no blocking empty state. |
| Error: Puter popup blocked | "Puter sign-in was blocked. Click 'Continue with Puter' directly — do not use keyboard shortcuts." |
| Error: Puter auth failed | "Puter sign-in failed. Check your Puter.com account and try again." |
| Error: Google auth failed | "Google sign-in failed. Try again or use an API key instead." |
| Error: API key save failed | "Could not save API key. Check the key is valid and try again." |
| Error: generic setup failure | "Setup failed. Please try again." |
| Puter auth button loading | "Connecting to Puter…" |
| Google auth button loading | "Signing in…" |
| API key submit loading | "Saving…" |
| Auto-detected tool badge | "{ToolName} detected" (e.g. "Hermes detected", "Claude Code detected") |
| Step indicator | "Step {N} of 4" |

Destructive actions: None in this phase. The Google OAuth risk warning is a caution, not a destructive action confirmation (no data is deleted).

---

## Registry Safety

| Registry | Blocks Used | Safety Gate |
|----------|-------------|-------------|
| shadcn official (ui.shadcn.com) | button, input, label, skeleton, dialog | not required |
| Third-party | none | not applicable |

No third-party shadcn registry blocks are used in this phase. `@heyputer/puter.js` is an npm package loaded via CDN script tag at runtime (not a shadcn registry block) — registry vetting gate does not apply.

---

## Notes for Executor

1. **Wizard step count:** Update the "Step N of 3" indicator to "Step N of 4" in `NexusOnboardingWizard.tsx`. The existing step counter is a plain string — a one-line change.

2. **Puter CDN load:** Load `https://js.puter.com/v2/` dynamically via `loadScript()` helper only when the user clicks "Continue with Puter" — not on wizard open. This avoids loading a large third-party script unnecessarily.

3. **Google risk warning timing gate:** The "Sign in with Google" button must remain disabled for 3 seconds after the Google card is selected to prevent accidental clicks. Use a `useEffect` with a `setTimeout` keyed to the selected card state.

4. **Provider card pre-detection:** Run `agentsApi.probeAdapter()` for `claude_local`, `hermes_local`, and `openclaw_gateway` when the wizard opens (parallel, fire-and-forget). Detected tools surface as a `text-xs text-primary` badge inside the card ("Hermes detected"). Non-detected tools show nothing extra.

5. **Skip-all valid state:** When the user skips Step 3 entirely, the wizard must still complete Step 4 (root directory) and create at least one working agent. The existing `defaultAdapter` probe result handles this — if hermes_local was detected, it's used; otherwise claude_local.

6. **No new shadcn components needed:** All required UI elements are covered by the existing component inventory (button, input, label, skeleton). Do not install additional shadcn components for this phase.

---

## Checker Sign-Off

- [ ] Dimension 1 Copywriting: PASS
- [ ] Dimension 2 Visuals: PASS
- [ ] Dimension 3 Color: PASS
- [ ] Dimension 4 Typography: PASS
- [ ] Dimension 5 Spacing: PASS
- [ ] Dimension 6 Registry Safety: PASS

**Approval:** pending
