Public: anyone can read this; the crew can change it.

A repository's two-column layout uses base.ts grid utilities, and .repo-layout leaves the stylesheet #4783

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/4592-a-repository-s-two-column-layout-uses-ba → main 7 commits fork 2 threads · 2 unresolved

  • Merged. scout/4592-a-repository-s-two-column-layout-uses-ba was merged into main as f33180d.

Review

Sign in to review this change.
4 files changed +60 −9
docs/DESIGN.md −1
@@ -441,7 +441,6 @@ the same line (#4888).
441441 - `.policy-line`
442442 - `.repo-actions`
443443 - `.repo-desc`
444 - `.repo-layout`
445444 - `.repo-title`
446445 - `.slash`
447446 - `.sub`
src/web/pages/repo.ts +1 −1
@@ -734,7 +734,7 @@ export async function renderCode(
734734 <div class="mt">${readme}</div>`;
735735 return o.rail === undefined
736736 ? main
737 : html`<div class="repo-layout">
737 : html`<div class="split">

Fine. .split collapses to one column below 960px, so a phone gets main first and then About, in DOM order, which suits a keyboard and a screen reader. Check the sticky aside once at 960–1100px.

738738 <div>${main}</div>
739739 <aside aria-label="About">${o.rail}</aside>
740740 </div>`;
src/web/style/content.ts +1 −7
@@ -157,12 +157,6 @@ a.origin-chip:hover { border-color: var(--border-strong); color: var(--fg); }
157157
158158 .repo-actions { display: flex; align-items: center; gap: 6px; margin-left: auto; }
159159
160 .repo-layout { display: grid; gap: 16px; margin-top: 16px; align-items: start; }
161 @media (min-width: 960px) {
162 .repo-layout { grid-template-columns: minmax(0, 1fr) 280px; gap: 24px; }
163 .repo-layout > aside { position: sticky; top: calc(var(--topbar-h) + 16px); }
164 }
165
166160 .tok { white-space: nowrap; }
167161
168162 .filters { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
@@ -250,7 +244,7 @@ form.inline { display: inline; margin-left: auto; }
250244 *content*, so a column with a long unbreakable line pushes the page wider
251245 than the phone rather than letting its own ellipsis do the work. A zero
252246 min-width is what makes truncation inside a grid actually truncate. */
253 .split > *, .repo-layout > * { min-width: 0; }
247 .split > * { min-width: 0; }
254248
255249 /* The generic two-column page: content, then a sidebar that sticks. */
256250 .split { display: grid; gap: 16px; margin-top: 16px; align-items: start; }
test/repo-layout.test.ts added +58
@@ -0,0 +1,58 @@
1 /**
2 * A repository's code page lays main and its About rail with `.split`, the
3 * one two-column page; `.repo-layout` is gone (#4592).
4 */
5 import { afterAll, beforeAll, expect, test } from "bun:test";
6 import { mkdtemp, rm } from "node:fs/promises";
7 import { tmpdir } from "node:os";
8 import { join } from "node:path";
9 import { createRepo } from "../src/actions/repos.ts";
10 import { createApp, type App } from "../src/app.ts";
11 import { createUser } from "../src/core/users.ts";
12 import { repoDir } from "../src/git/repos.ts";
13 import { commitFiles } from "../src/git/write.ts";
14 import { serve, type Servers } from "../src/http/server.ts";
15 import { quietLogs } from "./helpers/globals.ts";
16
17 quietLogs();
18
19 let dataDir: string;
20 let app: App;
21 let servers: Servers;
22
23 beforeAll(async () => {
24 dataDir = await mkdtemp(join(tmpdir(), "forge-repo-layout-"));
25 app = createApp({ dataDir, port: 0, host: "forge.test" });
26 servers = await serve(app);
27 const made = await createUser(app.db, {
28 origin: "seed",
29 trusted: true,
30 username: "olive",
31 email: "[email protected]",
32 password: "a-good-passphrase",
33 adminIfFirst: true,
34 });
35 if (!made.ok) throw new Error(made.error);
36 const r = await createRepo(app, made.value, { name: "yard", visibility: "public" });
37 if (!r.ok) throw new Error(String(r.error));
38 const c = await commitFiles(repoDir(app.reposRoot, "olive", "yard"), {
39 branch: "main",
40 message: "first",
41 files: [{ path: "README.md", content: "# yard\n" }],
42 author: { name: "olive", email: "[email protected]" },
43 });
44 if (!c.ok) throw new Error("no commit");
45 });
46
47 afterAll(async () => {
48 await servers.stop();
49 await rm(dataDir, { recursive: true, force: true });
50 });
51
52 test("the code page wraps main and the About aside in .split, not .repo-layout", async () => {
53 const r = await fetch(`${servers.url}/olive/yard`);
54 expect(r.status).toBe(200);
55 const html = await r.text();
56 expect(html).not.toContain("repo-layout");
57 expect(html).toMatch(/<div class="split">\s*<div>[\s\S]*<\/div>\s*<aside aria-label="About">/);
58 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs