From 4ec247e6d29a60ec5206aae941e78b3819a21fd4 Mon Sep 17 00:00:00 2001 From: bnz183 Date: Fri, 19 Jun 2026 13:46:05 +0200 Subject: [PATCH] feat(studio): stage onboarding & settings (Phase 4c) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make Settings orient a new user in seconds and surface the next action: - Settings opens with a clear page title and is staged into steps ("Step 1 · Publishing" leads with publishing readiness as the obvious next action; "Step 2 · How it works"), with advanced configuration kept behind progressive disclosure. Subhead adapts to whether a blog is connected. - Tokenized the onboarding/settings surfaces onto the design system: spacing and radius tokens, and removed off-scale 10/11px font sizes in the readiness panel. Updates the settings e2e to assert the staged structure and readiness-first order. Stacked on feat/editor-conventions (Phase 4b). Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 5 ++ apps/studio/e2e/smoke.spec.ts | 25 +++++-- apps/studio/src/components/SettingsPanel.tsx | 27 ++++++- apps/studio/src/index.css | 78 ++++++++++++++------ 4 files changed, 103 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c3d422..4abf65a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,11 @@ All notable changes to SourceDraft are documented here. The project uses [Semant with an accessible in-Studio dialog (labelled fields, Enter to submit, Escape to cancel, bare-domain URLs gain `https://`). Removed the unused legacy `MarkdownToolbar`. +- **Onboarding / settings staging (Phase 4c)** — Settings now opens with a clear + title and is staged into steps that surface publishing readiness as the + obvious first action, with advanced configuration behind progressive + disclosure. Tokenized the onboarding/settings surfaces (spacing, radius, and + off-scale font sizes) onto the design system. ## v0.1.0 diff --git a/apps/studio/e2e/smoke.spec.ts b/apps/studio/e2e/smoke.spec.ts index 4369f04..0291db1 100644 --- a/apps/studio/e2e/smoke.spec.ts +++ b/apps/studio/e2e/smoke.spec.ts @@ -181,14 +181,29 @@ test.describe("Studio smoke", () => { await expect(page.getByRole("heading", { name: "Content quality" })).toBeVisible(); }); - test("settings setup health renders", async ({ page }) => { + test("settings is staged with readiness as the first step", async ({ page }) => { await enterDemoMode(page); await page.getByRole("button", { name: "Settings", exact: true }).click(); + + // Page is oriented: a clear title and staged steps. await expect( - page.getByRole("heading", { name: "Status & configuration" }), + page.getByRole("heading", { name: "Settings", level: 1 }), ).toBeVisible(); - await expect(page.getByRole("heading", { name: "Welcome to SourceDraft" })).toBeVisible(); - await expect(page.getByRole("heading", { name: "Publishing readiness" })).toBeVisible(); + await expect(page.getByText("Step 1 · Publishing")).toBeVisible(); + await expect(page.getByText("Step 2 · How it works")).toBeVisible(); + + // Publishing readiness (step 1) appears before the explainer (step 2). + const readiness = page.getByRole("heading", { name: "Publishing readiness" }); + const welcome = page.getByRole("heading", { name: "Welcome to SourceDraft" }); + await expect(readiness).toBeVisible(); + await expect(welcome).toBeVisible(); + const readinessBox = await readiness.boundingBox(); + const welcomeBox = await welcome.boundingBox(); + expect(readinessBox && welcomeBox).toBeTruthy(); + expect(readinessBox!.y).toBeLessThan(welcomeBox!.y); + + // Advanced config stays behind progressive disclosure. + await expect(page.getByRole("heading", { name: "Diagnostics" })).toBeHidden(); await page.locator("summary.settings-view__advanced-summary").click(); await expect(page.getByRole("heading", { name: "Diagnostics" })).toBeVisible(); await expect(page.getByRole("heading", { name: "Setup detection" })).toBeVisible(); @@ -204,7 +219,7 @@ test.describe("Studio smoke", () => { await nav.getByRole("button", { name: "Settings", exact: true }).click(); await expect( - page.getByRole("heading", { name: "Status & configuration" }), + page.getByRole("heading", { name: "Settings", level: 1 }), ).toBeVisible(); await expect( nav.getByRole("button", { name: "Settings", exact: true }), diff --git a/apps/studio/src/components/SettingsPanel.tsx b/apps/studio/src/components/SettingsPanel.tsx index f83ca76..3a099fe 100644 --- a/apps/studio/src/components/SettingsPanel.tsx +++ b/apps/studio/src/components/SettingsPanel.tsx @@ -10,17 +10,36 @@ type SettingsPanelProps = { }; export function SettingsPanel({ config }: SettingsPanelProps) { + const connected = + config.githubOwner.trim().length > 0 && config.githubRepo.trim().length > 0; + return (
+
+

Settings

+

+ {connected + ? "Your blog is connected. Check publishing readiness below before you send." + : "Connect your blog and confirm publishing is ready. Prefer to explore first? Demo mode needs no setup."} +

+
+
-

Status & configuration

- +

Step 1 · Publishing

+
+

Step 2 · How it works

+ +
+
Advanced configuration diff --git a/apps/studio/src/index.css b/apps/studio/src/index.css index cbfc003..8a79e4c 100644 --- a/apps/studio/src/index.css +++ b/apps/studio/src/index.css @@ -317,13 +317,45 @@ code { .settings-view { display: flex; flex-direction: column; - gap: 16px; + gap: var(--space-5); +} + +.settings-view__intro { + display: flex; + flex-direction: column; + gap: var(--space-1); +} + +.settings-view__heading { + font-size: var(--text-2xl); + font-weight: 600; + color: var(--text); +} + +.settings-view__subhead { + font-size: var(--text-base); + color: var(--text-muted); + max-width: 60ch; +} + +.settings-view__step { + display: flex; + flex-direction: column; + gap: var(--space-2); +} + +.settings-view__step-eyebrow { + font-size: var(--text-xs); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.04em; + color: var(--accent); } .settings-view__group { display: flex; flex-direction: column; - gap: 12px; + gap: var(--space-3); } .settings-view__group-title { @@ -1959,7 +1991,7 @@ select.field__input:focus-visible { .settings-panel__note { margin: 0; - padding: 12px 14px; + padding: var(--space-3) var(--space-4); font-size: var(--text-sm); color: var(--text-muted); line-height: 1.55; @@ -1969,8 +2001,8 @@ select.field__input:focus-visible { .settings-panel__grid { display: grid; grid-template-columns: 1fr 1fr; - gap: 12px; - padding: 14px; + gap: var(--space-3); + padding: var(--space-4); } .validation-panel { @@ -2327,14 +2359,14 @@ select.field__input:focus-visible { flex-wrap: wrap; align-items: flex-start; justify-content: space-between; - gap: 12px; - padding: 12px 16px; + gap: var(--space-3); + padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border); background: var(--bg-raised); } .post-login-welcome__title { - margin: 0 0 4px; + margin: 0 0 var(--space-1); font-size: var(--text-sm); font-weight: 600; color: var(--text); @@ -2349,8 +2381,8 @@ select.field__input:focus-visible { } .post-login-welcome__tips { - margin: 8px 0 0; - padding-left: 18px; + margin: var(--space-2) 0 0; + padding-left: var(--space-5); font-size: var(--text-xs); color: var(--text-muted); line-height: 1.45; @@ -2359,12 +2391,12 @@ select.field__input:focus-visible { .post-login-welcome__actions { display: flex; flex-wrap: wrap; - gap: 8px; + gap: var(--space-2); flex-shrink: 0; } .settings-view__advanced { - margin-top: 8px; + margin-top: var(--space-2); } .settings-view__advanced-summary { @@ -2376,10 +2408,10 @@ select.field__input:focus-visible { } .setup-health__advanced { - margin-top: 12px; + margin-top: var(--space-3); display: flex; flex-direction: column; - gap: 10px; + gap: var(--space-2); } .setup-health__advanced-toggle { @@ -2389,8 +2421,8 @@ select.field__input:focus-visible { .login-screen__form { display: flex; flex-direction: column; - gap: 14px; - padding: 14px; + gap: var(--space-4); + padding: var(--space-4); } .login-screen__intro { @@ -2482,7 +2514,7 @@ select.field__input:focus-visible { } .setup-health__next-action { - margin-bottom: 12px; + margin-bottom: var(--space-3); } .setup-health__list { @@ -2491,17 +2523,17 @@ select.field__input:focus-visible { padding: 0; display: flex; flex-direction: column; - gap: 8px; + gap: var(--space-2); } .setup-health__item { display: grid; grid-template-columns: 88px 140px minmax(0, 1fr); - gap: 10px; + gap: var(--space-2); align-items: start; - padding: 10px 12px; + padding: var(--space-2) var(--space-3); border: 1px solid var(--border); - border-radius: 6px; + border-radius: var(--radius-md); background: var(--bg); } @@ -2516,7 +2548,7 @@ select.field__input:focus-visible { } .setup-health__status { - font-size: 10px; + font-size: var(--text-xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; @@ -2529,7 +2561,7 @@ select.field__input:focus-visible { } .setup-health__detail { - font-size: 11px; + font-size: var(--text-xs); color: var(--text-muted); line-height: 1.45; }