Fork page UX: top-level route, copy cleanup, mobile collapse, mobile-first rule, aligned Fork/Clone buttons #438

Closed
opened 2026-09-13 13:05:46 +00:00 by crueber · 3 comments
Owner

Fork page UX: top-level route, copy cleanup, mobile collapse, mobile-first AGENTS.md amendment, aligned Fork/Clone buttons

What's requested

One coherent UX pass over fork creation and the repo-header action row, five parts:

  1. Fork becomes a top-level route (PR-composer style), not a nested repo-page route.
    Today the fork form lives at /:owner/:name/fork as a child of the Repo layout (web/src/index.jsx, inside the /:owner/:name Route). The PR composer (/:owner/:name/pulls/new → web/src/pages/PullNew.jsx) is the in-app precedent for a full-page composer, but the ask is stronger: move Fork OUT of the repo page shell to a top-level route like GitHub's /fork flow, so it renders as a standalone page (site header + page-level layout), not inside the repo tab strip/sidebar. Keep the parent repo as a query/route param (e.g. /:owner/:name/fork → top-level page, or a ?from= param — planner's call, note it). The repo-page "Fork" pill links there.

  2. Copy cleanup on the fork form (web/src/pages/Fork.jsx):

    • Remove the owner hint <span class="muted text-xs">you and your orgs only</span> (line ~207).
    • Remove the branch hint <span class="muted text-xs">becomes the fork's default branch</span> (line ~252).
    • Rename "Starting branch" → "Default branch" (labels + aria-labels, lines ~240/241/246). The <option value="">parent default</option> stays.
  3. Mobile field-collapse fix. The two grid grid-cols-2 gap-3 rows (Owner/Name ~line 187, Visibility/Default branch ~line 225) force side-by-side fields at phone widths; on narrow screens each pair should collapse to a single column (responsive: grid-cols-1 base, sm:grid-cols-2). Verify no horizontal overflow at a 390px viewport after the change.

  4. AGENTS.md amendment: mobile is always considered. Add a working rule to AGENTS.md §2 (Working rules, after the "Real browser when you touched anything browser-facing" verification item or as a §2 bullet) stating that every browser-facing change is checked at a narrow/mobile viewport (e.g. ~390px width), not just desktop — UI work without a mobile-viewport check is not done. Draft text; final wording may be tightened by the implementer but the rule must be normative (same register as the existing laws/working rules).

  5. Aligned text-only Fork/Clone buttons (web/src/pages/Repo.jsx, repo header action row ~line 618-628): the Fork pill (⑂ Fork N) and the Clone pill/CloneMenu summary render as differently-sized/wide pills with an icon glyph on Fork. Make both text-only and visually aligned — same pill class, same height/weight, no icon glyph, so they read as one matched action pair (the Star/Watch toggles already establish the row's text-pill idiom; match it).

Evidence (current tree, main @ ec820c5)

  • web/src/index.jsx — <Route path="/fork" component={Fork} /> nested inside <Route path="/:owner/:name" component={Repo}>.
  • web/src/pages/Fork.jsx header comment already ties the page to issue #424; the form rows at lines ~187 and ~225 use grid grid-cols-2; hints at lines 207 and 252; "Starting branch" at 240/241/246.
  • web/src/pages/Repo.jsx ~line 620: <A class="pill" href={/${full()}/fork}>⑂ Fork…</A>; CloneMenu summary uses class="pill" (line ~99).

Architecture notes

  • Pure SPA change: no API/wire change, no ETag concern. The SDK client (repos.repo(full())) already supports the fork call — Fork.jsx keeps working unchanged if the route shape keeps owner/name in params.
  • Moving Fork out of the Repo Route means it loses the repo layout (tab strip, shared summary); that is the point — replicate PullNew's page-level framing, but NOT nested under Repo.
  • The Fork pill count on Repo.jsx reads summary.forks (no extra fetch) — keep that.

Acceptance criteria

  • Fork form renders as a standalone top-level page (site header only, no repo tab strip/sidebar), reachable from the repo header Fork pill; old nested route redirects or is replaced without a dead link.
  • "you and your orgs only" and "becomes the fork's default branch" hints removed; "Starting branch" is now "Default branch" everywhere (visible label + aria-labels).
  • At ~390px viewport the fork form shows no horizontal page overflow and all fields stack single-column.
  • AGENTS.md contains a normative mobile-viewport rule for browser-facing changes.
  • Repo header Fork and Clone controls are text-only, same pill idiom, visually aligned as a pair.
  • make test-web passes; real-browser check at desktop and 390px widths for the fork flow and the repo header.
# Fork page UX: top-level route, copy cleanup, mobile collapse, mobile-first AGENTS.md amendment, aligned Fork/Clone buttons ## What's requested One coherent UX pass over fork creation and the repo-header action row, five parts: 1. **Fork becomes a top-level route (PR-composer style), not a nested repo-page route.** Today the fork form lives at `/:owner/:name/fork` as a child of the `Repo` layout (`web/src/index.jsx`, inside the `/:owner/:name` Route). The PR composer (`/:owner/:name/pulls/new` → `web/src/pages/PullNew.jsx`) is the in-app precedent for a full-page composer, but the ask is stronger: move Fork OUT of the repo page shell to a top-level route like GitHub's `/fork` flow, so it renders as a standalone page (site header + page-level layout), not inside the repo tab strip/sidebar. Keep the parent repo as a query/route param (e.g. `/:owner/:name/fork` → top-level page, or a `?from=` param — planner's call, note it). The repo-page "Fork" pill links there. 2. **Copy cleanup on the fork form** (`web/src/pages/Fork.jsx`): - Remove the owner hint `<span class="muted text-xs">you and your orgs only</span>` (line ~207). - Remove the branch hint `<span class="muted text-xs">becomes the fork's default branch</span>` (line ~252). - Rename "Starting branch" → "Default branch" (labels + `aria-label`s, lines ~240/241/246). The `<option value="">parent default</option>` stays. 3. **Mobile field-collapse fix.** The two `grid grid-cols-2 gap-3` rows (Owner/Name ~line 187, Visibility/Default branch ~line 225) force side-by-side fields at phone widths; on narrow screens each pair should collapse to a single column (responsive: `grid-cols-1` base, `sm:grid-cols-2`). Verify no horizontal overflow at a 390px viewport after the change. 4. **AGENTS.md amendment: mobile is always considered.** Add a working rule to AGENTS.md §2 (Working rules, after the "Real browser when you touched anything browser-facing" verification item or as a §2 bullet) stating that every browser-facing change is checked at a narrow/mobile viewport (e.g. ~390px width), not just desktop — UI work without a mobile-viewport check is not done. Draft text; final wording may be tightened by the implementer but the rule must be normative (same register as the existing laws/working rules). 5. **Aligned text-only Fork/Clone buttons** (`web/src/pages/Repo.jsx`, repo header action row ~line 618-628): the Fork pill (`⑂ Fork N`) and the Clone pill/`CloneMenu` summary render as differently-sized/wide pills with an icon glyph on Fork. Make both text-only and visually aligned — same pill class, same height/weight, no icon glyph, so they read as one matched action pair (the Star/Watch toggles already establish the row's text-pill idiom; match it). ## Evidence (current tree, main @ ec820c5) - `web/src/index.jsx` — `<Route path="/fork" component={Fork} />` nested inside `<Route path="/:owner/:name" component={Repo}>`. - `web/src/pages/Fork.jsx` header comment already ties the page to issue #424; the form rows at lines ~187 and ~225 use `grid grid-cols-2`; hints at lines 207 and 252; "Starting branch" at 240/241/246. - `web/src/pages/Repo.jsx` ~line 620: `<A class="pill" href={`/${full()}/fork`}>⑂ Fork…</A>`; `CloneMenu` summary uses `class="pill"` (line ~99). ## Architecture notes - Pure SPA change: no API/wire change, no ETag concern. The SDK client (`repos.repo(full())`) already supports the fork call — Fork.jsx keeps working unchanged if the route shape keeps owner/name in params. - Moving Fork out of the `Repo` Route means it loses the repo layout (tab strip, shared summary); that is the point — replicate PullNew's page-level framing, but NOT nested under Repo. - The Fork pill count on Repo.jsx reads `summary.forks` (no extra fetch) — keep that. ## Acceptance criteria - [ ] Fork form renders as a standalone top-level page (site header only, no repo tab strip/sidebar), reachable from the repo header Fork pill; old nested route redirects or is replaced without a dead link. - [ ] "you and your orgs only" and "becomes the fork's default branch" hints removed; "Starting branch" is now "Default branch" everywhere (visible label + aria-labels). - [ ] At ~390px viewport the fork form shows no horizontal page overflow and all fields stack single-column. - [ ] AGENTS.md contains a normative mobile-viewport rule for browser-facing changes. - [ ] Repo header Fork and Clone controls are text-only, same pill idiom, visually aligned as a pair. - [ ] `make test-web` passes; real-browser check at desktop and 390px widths for the fork flow and the repo header.
crueber added this to the v1 milestone 2026-09-13 13:06:06 +00:00
Author
Owner

Fix ready for review: PR #441 (#441), branch fix/issue-438. All 6 acceptance criteria covered (5 code + test/build); browser proof at desktop + 390px open (shared-daemon loopback guard). Tests: node --test 919 total / 917 pass / 2 fail (pre-existing live-server smoke, identical on main); vite + esbuild green.

Fix ready for review: PR #441 (https://git.packden.us/crueber/walhub/pulls/441), branch fix/issue-438. All 6 acceptance criteria covered (5 code + test/build); browser proof at desktop + 390px open (shared-daemon loopback guard). Tests: node --test 919 total / 917 pass / 2 fail (pre-existing live-server smoke, identical on main); vite + esbuild green.
Author
Owner

Review of PR #441 (origin/fix/issue-438 @ 3afa46d) — verified in scratch worktrees /tmp/pr441 (branch) and /tmp/pr441-main (main baseline). No fixes pushed; none needed.

(1) Top-level route — PASS. web/src/index.jsx:94 adds <Route path="/:owner/:name/fork" component={Fork} /> BEFORE the /:owner/:name Repo parent, under the shared Router root={App} (index.jsx:53) so it renders standalone under the site header only, no repo tab strip/sidebar. Old nested <Route path="/fork"/> is deleted; exactly one component={Fork} entry remains — no duplicate, no dead link. Same URL as before so the repo-header Fork pill (Repo.jsx:624) and all existing links keep working, no redirect needed. Standalone-safe: Fork.jsx reads owner/name only via useParams() (:30), zero useRepo/Repo-context references; page centers itself via mx-auto (Fork.jsx:182). web/sdk untouched.

(2) Copy — PASS. Both hints deleted (no 'you and your orgs only', no 'becomes the fork's default branch' anywhere in Fork.jsx); 'Starting branch' fully gone, 'Default branch' is the visible label (Fork.jsx:243) plus both branch-select aria-labels (loading fallback :244, loaded :249 — test pins count=2); <option value="">parent default</option> kept.

(3) Mobile — PASS. Both field rows now grid grid-cols-1 gap-3 sm:grid-cols-2 (Fork.jsx:191 Owner/Name, :228 Visibility/Default branch); no unprefixed grid-cols-2 gap-3 remains. 390px reasoning: form cell = 390−32 (page px-4) = 358px; single-column rows are one fluid field wide, all controls fluid input/select, no fixed-width elements → zero page overflow. (Per review instructions: node tests + reasoning only, no browser driven — noted explicitly.)

(4) AGENTS.md rule — PASS. AGENTS.md:51, §2 working-rules bullet: 'Mobile viewport is always checked. Every browser-facing change is verified at a narrow/mobile viewport (~390px width) as well as desktop — layout, overflow, and tap targets. UI work without a mobile-viewport check is not done.' Normative register matching the surrounding rules; §2-bullet placement satisfies the issue's 'verification item or §2 bullet' planner's call.

(5) Pill pair — PASS. Fork (Repo.jsx:624-626) is text-only class="pill" (⑂ glyph gone), CloneMenu summary is class="pill ...">Clone (Repo.jsx:99) — same pill class ⇒ same height/weight, matched text-only pair per the Star/Watch idiom. Count still s().forks (no extra fetch); Fork href /${full()}/fork unchanged.

(6) Scope/docs — PASS. No .go files touched; no package.json/pnpm changes (no new deps); SDK untouched. docs/go/12_web_ui.md:229 route-table entry + decisions entry both accurate (counts verified below; 'esbuild green' not re-run — SDK untouched so bundle rebuild is a no-op, vite build confirmed green).

Tests (scratch worktree, node_modules symlinked from main): fork-page-438.test.js 6/6 pass. Full node --test web/test/unit/*.test.js: branch 919 total / 917 pass / 2 fail; main baseline /tmp/pr441-main: 913 total / 911 pass / 2 fail — identical smoke.test.js live-server failures on pristine main (needs a server serving the build; something unrelated answers on :8080, left untouched), so +6 net new, zero PR-caused. Doc test-count accounting is exact. vite build green (2.21s). Main worktree left clean/read-only (fetch + worktrees only).

MERGE RECOMMENDATION: ready to merge. Only open item (as the PR itself notes): real-browser proof at desktop + 390px — same standing exception as #435/#437, not a blocker for this change.

Review of PR #441 (origin/fix/issue-438 @ 3afa46d) — verified in scratch worktrees /tmp/pr441 (branch) and /tmp/pr441-main (main baseline). No fixes pushed; none needed. (1) Top-level route — PASS. web/src/index.jsx:94 adds `<Route path="/:owner/:name/fork" component={Fork} />` BEFORE the `/:owner/:name` Repo parent, under the shared `Router root={App}` (index.jsx:53) so it renders standalone under the site header only, no repo tab strip/sidebar. Old nested `<Route path="/fork"/>` is deleted; exactly one `component={Fork}` entry remains — no duplicate, no dead link. Same URL as before so the repo-header Fork pill (Repo.jsx:624) and all existing links keep working, no redirect needed. Standalone-safe: Fork.jsx reads owner/name only via useParams() (:30), zero useRepo/Repo-context references; page centers itself via mx-auto (Fork.jsx:182). web/sdk untouched. (2) Copy — PASS. Both hints deleted (no 'you and your orgs only', no 'becomes the fork's default branch' anywhere in Fork.jsx); 'Starting branch' fully gone, 'Default branch' is the visible label (Fork.jsx:243) plus both branch-select aria-labels (loading fallback :244, loaded :249 — test pins count=2); `<option value="">parent default</option>` kept. (3) Mobile — PASS. Both field rows now `grid grid-cols-1 gap-3 sm:grid-cols-2` (Fork.jsx:191 Owner/Name, :228 Visibility/Default branch); no unprefixed `grid-cols-2 gap-3` remains. 390px reasoning: form cell = 390−32 (page px-4) = 358px; single-column rows are one fluid field wide, all controls fluid input/select, no fixed-width elements → zero page overflow. (Per review instructions: node tests + reasoning only, no browser driven — noted explicitly.) (4) AGENTS.md rule — PASS. AGENTS.md:51, §2 working-rules bullet: 'Mobile viewport is always checked. Every browser-facing change is verified at a narrow/mobile viewport (~390px width) as well as desktop — layout, overflow, and tap targets. UI work without a mobile-viewport check is not done.' Normative register matching the surrounding rules; §2-bullet placement satisfies the issue's 'verification item or §2 bullet' planner's call. (5) Pill pair — PASS. Fork (Repo.jsx:624-626) is text-only `class="pill"` (⑂ glyph gone), CloneMenu summary is `class="pill ...">Clone` (Repo.jsx:99) — same pill class ⇒ same height/weight, matched text-only pair per the Star/Watch idiom. Count still `s().forks` (no extra fetch); Fork href `/${full()}/fork` unchanged. (6) Scope/docs — PASS. No .go files touched; no package.json/pnpm changes (no new deps); SDK untouched. docs/go/12_web_ui.md:229 route-table entry + decisions entry both accurate (counts verified below; 'esbuild green' not re-run — SDK untouched so bundle rebuild is a no-op, vite build confirmed green). Tests (scratch worktree, node_modules symlinked from main): fork-page-438.test.js 6/6 pass. Full `node --test web/test/unit/*.test.js`: branch 919 total / 917 pass / 2 fail; main baseline /tmp/pr441-main: 913 total / 911 pass / 2 fail — identical smoke.test.js live-server failures on pristine main (needs a server serving the build; something unrelated answers on :8080, left untouched), so +6 net new, zero PR-caused. Doc test-count accounting is exact. `vite build` green (2.21s). Main worktree left clean/read-only (fetch + worktrees only). MERGE RECOMMENDATION: ready to merge. Only open item (as the PR itself notes): real-browser proof at desktop + 390px — same standing exception as #435/#437, not a blocker for this change.
Author
Owner

Fixed by PR #441 (review clean — all 6 criteria pass, route hoist + copy + mobile + pill pair + AGENTS.md rule verified), merged. Closing.

Fixed by PR #441 (review clean — all 6 criteria pass, route hoist + copy + mobile + pill pair + AGENTS.md rule verified), merged. Closing.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
crueber/walhub#438
No description provided.