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

Design: a repository's about column uses §5's card, and about leaves §5d #4050

Merged opened this

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

Merged

scout/forge:scout/3952-design-a-repository-s-about-column-uses → main 11 commits fork 4 threads · 4 unresolved

  • Merged. scout/3952-design-a-repository-s-about-column-uses was merged into main as aef32ca.

Review

Sign in to review this change.
6 files changed +81 −67
docs/DESIGN.md +1 −1
@@ -393,7 +393,7 @@ gone, so this list only shrinks.
393393 - **`style/base.ts`** — the page shell (topbar, main, footer) and the layout and text utilities every page leans on: `.bar` `.bare` `.foot` `.grow` `.hide-lg` `.hide-sm` `.js-only` `.live-pill` `.mark` `.mb` `.mt` `.muted` `.narrow` `.narrow-wide` `.num` `.reduced` `.shell` `.shell-wide` `.skip` `.small` `.spacer` `.sr-only` `.stack` `.stack-tight` `.topbar` `.topbar-tools` `.truncate` `.wordmark`
394394 - **`style/components.ts`** — parts of the §5 components that the table does not spell out — the palette's list, the menu's items, a dialog's head and foot, comments, the theme picker — and a few small helpers beside them: `.cat` `.clone` `.clone-text` `.comment` `.comment-body` `.comment-form` `.dash` `.dialog-body` `.dialog-foot` `.dialog-head` `.empty-title` `.error-note` `.field-row` `.form-actions` `.hex` `.ico` `.ico-loader` `.list` `.live-status` `.menu-item` `.menu-panel` `.menu-sep` `.menu-title` `.palette-empty` `.palette-foot` `.palette-group` `.palette-heading` `.palette-input` `.palette-item` `.palette-list` `.passkey` `.passkey-lead` `.passkey-note` `.passkey-off` `.passkey-on` `.preview` `.sep` `.split` `.sprite` `.swatch` `.table-scroll` `.tell-here` `.theme-apply` `.theme-option` `.tick` `.timeline` `.tl-dot` `.toast-body` `.topbar-search`
395395 - **`style/components.ts`, settings rows** — a list whose rows are settings (limits, capacity): the `<ul class="list setting-list">` lets title and meta wrap instead of ellipsizing, and a row's controls take their own line under 640px. `.row-fields` is the form in that row's trail (input, Save, Default): always visible, never hover-gated, wrapping under the text before it would clip. Use both for any page whose rows show a name, a current value, and a control to change it; use `.row-actions` only for secondary actions (delete, wake, pin, revoke). Flags, telemetry, cloud, people, crew roles and subscription headroom/budget use `.row-fields`. Classes: `.row-fields` `.setting-list`
396 - **`style/content.ts`, page furniture** — what frames a page's content: page and repo heads, the merge box, the front page and its tiles, explore, backups, boards, the fleet chart's series colours, following, lineage: `.about` `.act` `.app-shelf` `.app-tile` `.app-tile-body` `.app-tile-desc` `.app-tile-look` `.app-tile-name` `.backups-drill` `.backups-error` `.backups-now` `.backups-restore` `.bform` `.bgrid` `.big` `.bmanage` `.boards-result` `.bp-h2` `.bp-h3` `.bp-w2` `.bp-w3` `.break` `.bt-bar` `.bt-grid` `.bt-group` `.bt-hist` `.bt-name` `.bt-procs` `.bt-row` `.bt-val` `.chart` `.chart-box` `.commit-body` `.commit-head` `.connect-more` `.connect-path` `.domain-records` `.explore-filters` `.explore-line` `.explore-main` `.explore-name` `.explore-row` `.fg-s0` `.fg-s1` `.fg-s2` `.fg-s3` `.fg-s4` `.fg-s5` `.fg-s6` `.fg-s7` `.filters` `.flag-form` `.following` `.following-actions` `.following-eyebrow` `.following-foot` `.following-head` `.following-sentence` `.fork-bar` `.front-admin` `.front-controls` `.front-crew` `.front-featured` `.front-featured-body` `.front-featured-look` `.front-issues` `.front-live` `.front-more` `.front-pick` `.front-pin-any` `.front-weights` `.frozen` `.frozen-form` `.frozen-sentence` `.handoff` `.handoff-links` `.headroom` `.issue-head` `.issue-meta` `.lineage` `.lineage-children` `.lineage-here` `.merge-box` `.merge-form` `.merge-headline` `.merge-sub` `.mirror-form` `.number` `.origin-link` `.origin-mark` `.origin-name` `.origin-row` `.owner` `.page-head` `.path-head` `.policy-line` `.qf` `.repo-actions` `.repo-audience` `.repo-desc` `.repo-layout` `.repo-lineage` `.repo-title` `.search` `.slash` `.stage-detail` `.stage-dot` `.steps` `.sub` `.sys-pair` `.table-wrap` `.terminal-line` `.tok` `.tree-row` `.tree-total` `.wall-cells` `.where-sep`
396 - **`style/content.ts`, page furniture** — what frames a page's content: page and repo heads, the merge box, the front page and its tiles, explore, backups, boards, the fleet chart's series colours, following, lineage: `.act` `.app-shelf` `.app-tile` `.app-tile-body` `.app-tile-desc` `.app-tile-look` `.app-tile-name` `.backups-drill` `.backups-error` `.backups-now` `.backups-restore` `.bform` `.bgrid` `.big` `.bmanage` `.boards-result` `.bp-h2` `.bp-h3` `.bp-w2` `.bp-w3` `.break` `.bt-bar` `.bt-grid` `.bt-group` `.bt-hist` `.bt-name` `.bt-procs` `.bt-row` `.bt-val` `.chart` `.chart-box` `.commit-body` `.commit-head` `.connect-more` `.connect-path` `.domain-records` `.explore-filters` `.explore-line` `.explore-main` `.explore-name` `.explore-row` `.fg-s0` `.fg-s1` `.fg-s2` `.fg-s3` `.fg-s4` `.fg-s5` `.fg-s6` `.fg-s7` `.filters` `.flag-form` `.following` `.following-actions` `.following-eyebrow` `.following-foot` `.following-head` `.following-sentence` `.fork-bar` `.front-admin` `.front-controls` `.front-crew` `.front-featured` `.front-featured-body` `.front-featured-look` `.front-issues` `.front-live` `.front-more` `.front-pick` `.front-pin-any` `.front-weights` `.frozen` `.frozen-form` `.frozen-sentence` `.handoff` `.handoff-links` `.headroom` `.issue-head` `.issue-meta` `.lineage` `.lineage-children` `.lineage-here` `.merge-box` `.merge-form` `.merge-headline` `.merge-sub` `.mirror-form` `.number` `.origin-link` `.origin-mark` `.origin-name` `.origin-row` `.owner` `.page-head` `.path-head` `.policy-line` `.qf` `.repo-actions` `.repo-audience` `.repo-desc` `.repo-layout` `.repo-lineage` `.repo-title` `.search` `.slash` `.stage-detail` `.stage-dot` `.steps` `.sub` `.sys-pair` `.table-wrap` `.terminal-line` `.tok` `.tree-row` `.tree-total` `.wall-cells` `.where-sep`
outdated

.kv re-enters §5d here after #3896 removed it. It should stay out.

397397 - **`style/content.ts`, code view** — the source view and the syntax-highlight token classes `highlight-html.ts` builds as `tok-${kind}`: `.blob-view` `.ln` `.tok-attribute` `.tok-comment` `.tok-constant` `.tok-decorator` `.tok-deleted` `.tok-emphasis` `.tok-function` `.tok-heading` `.tok-inserted` `.tok-keyword` `.tok-link` `.tok-meta` `.tok-number` `.tok-operator` `.tok-plain` `.tok-property` `.tok-punctuation` `.tok-regex` `.tok-string` `.tok-tag` `.tok-type` `.tok-variable`
398398 - **`style/content.ts`, prose (markdown)** — table cell alignment, built by `markdown.ts` as `ta-${align}`: `.ta-c` `.ta-r`
399399 - **`style/content.ts`, diff viewer** — the diff table: its gutters, line kinds, file head and inline review comments: `.add` `.del` `.dh` `.diff` `.diff-body` `.diff-head` `.diff-note` `.diff-path` `.diff-stat` `.diff-stub` `.diff-summary` `.diff-table` `.dl` `.dl-review` `.dn` `.dn-comment` `.nonl` `.rc-hidden` `.rc-resolve` `.rc-resolved` `.wd`
src/web/pages/browse.ts +23 −22
@@ -66,6 +66,7 @@ import {
6666 listCard,
6767 originTag,
6868 pager,
69 row,
6970 seg,
7071 stateBadge,
7172 time,
@@ -556,32 +557,33 @@ export async function browseCommitPage(ctx: Ctx): Promise<Response> {
556557 function aboutRail(scope: RepoScope): Html {
557558 const facts = scope.facts;
558559 const origin = scope.origin;
559 return html`<aside class="about">
560 ${card(
560 return html`${card(
561561 html`${when(
562562 facts.description !== "",
563563 () => html`<p class="mb">${facts.description}</p>`,
564564 )}
565565 <div class="where-block">
566566 <h3>Where it lives</h3>
567 <dl>
568 <dt>Origin</dt>
569 <dd>
570 ${scope.originUrl === ""
571 ? html`${scope.fullName}`
572 : html`<a href="${scope.originUrl}" rel="noreferrer noopener"
573 >${scope.fullName}</a
574 >`}
575 </dd>
576 <dt>Last push</dt>
577 <dd>
578 ${facts.pushedAt === null ? "unknown" : time(facts.pushedAt)}
579 </dd>
580 <dt>Visibility</dt>
581 <dd>${facts.visibility}</dd>
582 <dt>Default</dt>
583 <dd class="mono">${facts.defaultBranch}</dd>
584 </dl>
567 <ul class="list setting-list">
568 ${row({
569 title: "Origin",
570 trail:
571 scope.originUrl === ""
572 ? html`${scope.fullName}`
573 : html`<a href="${scope.originUrl}" rel="noreferrer noopener"
574 >${scope.fullName}</a
575 >`,
576 })}
577 ${row({
578 title: "Last push",
579 trail: html`${facts.pushedAt === null ? "unknown" : time(facts.pushedAt)}`,
580 })}
581 ${row({ title: "Visibility", trail: html`${facts.visibility}` })}
582 ${row({
583 title: "Default",
584 trail: html`<span class="mono">${facts.defaultBranch}</span>`,
585 })}
586 </ul>
585587 <p class="mt small muted">
586588 Nothing is stored here yet. Mirror it and this becomes a repository
587589 you own — issues, pulls and comments come with it, and pushes go
@@ -606,8 +608,7 @@ function aboutRail(scope: RepoScope): Html {
606608 )}
607609 </div>`,
608610 { head: html`<h2>About</h2>` },
609 )}
610 </aside>`;
611 )}`;
611612 }
612613
613614 // ---- issues and pulls ----
src/web/pages/repo.ts +46 −40
@@ -571,18 +571,21 @@ function aboutCard(
571571 (o) =>
572572 html`<div class="where-block">
573573 <h3>Where it lives</h3>
574 <dl>
575 <dt>Origin</dt>
576 <dd>
577 ${o.htmlUrl === ""
578 ? html`${o.fullName}`
579 : html`<a href="${o.htmlUrl}" rel="noreferrer noopener"
580 >${o.fullName}</a
581 >`}
582 </dd>
583 <dt>Last fetch</dt>
584 <dd>${o.lastFetchAt === null ? "never" : time(o.lastFetchAt)}</dd>
585 </dl>
574 <ul class="list setting-list">
575 ${row({
576 title: "Origin",
577 trail:
578 o.htmlUrl === ""
579 ? html`${o.fullName}`
580 : html`<a href="${o.htmlUrl}" rel="noreferrer noopener"
581 >${o.fullName}</a
582 >`,
583 })}
584 ${row({
585 title: "Last fetch",
586 trail: html`${o.lastFetchAt === null ? "never" : time(o.lastFetchAt)}`,
587 })}
588 </ul>
586589 ${when(
587590 o.fetchError !== "",
588591 () => html`<p class="error-note mt">${o.fetchError}</p>`,
@@ -601,50 +604,54 @@ function aboutCard(
601604 </p>
602605 </div>`,
603606 );
604 return html`<aside class="about">
605 ${card(
607 return html`${card(
606608 html`${when(
607609 repo.description !== "",
608610 () => html`<p class="mb">${repo.description}</p>`,
609611 )}
610 <dl>
611 <dt>Visibility</dt>
612 <dd>${repo.visibility}</dd>
613 <dt>Default</dt>
614 <dd class="mono">${repo.defaultBranch}</dd>
615 <dt>Protection</dt>
616 <dd>${repo.protectDefault ? "on" : "off"}</dd>
617 <dt>Branches</dt>
618 <dd class="num">${refs.branches.length}</dd>
612 <ul class="list setting-list">
613 ${row({ title: "Visibility", trail: html`${repo.visibility}` })}
614 ${row({
615 title: "Default",
616 trail: html`<span class="mono">${repo.defaultBranch}</span>`,
617 })}
618 ${row({ title: "Protection", trail: html`${repo.protectDefault ? "on" : "off"}` })}
619 ${row({
620 title: "Branches",
621 trail: html`<span class="num">${refs.branches.length}</span>`,
622 })}
619623 ${when(
620624 refs.tags.length > 0,
621625 () =>
622 html`<dt>Tags</dt>
623 <dd class="num">${refs.tags.length}</dd>`,
626 row({
627 title: "Tags",
628 trail: html`<span class="num">${refs.tags.length}</span>`,
629 }),
624630 )}
625631 ${when(
626632 parent,
627633 (p) =>
628634 // The standing and the one thing to do about it live in the
629635 // Following card above the code; the rail only names the parent.
630 html`<dt>Fork of</dt>
631 <dd>
632 <a href="/${p.ownerName}/${p.name}"
633 >${p.ownerName}/${p.name}</a
634 >
635 </dd>`,
636 row({
637 title: "Fork of",
638 trail: html`<a href="/${p.ownerName}/${p.name}"
639 >${p.ownerName}/${p.name}</a
640 >`,
641 }),
636642 )}
637643 ${when(
638644 upstream,
639645 (up) =>
640 html`<dt>Fork of</dt>
641 <dd>
642 ${upstreamHref === undefined
646 row({
647 title: "Fork of",
648 trail:
649 upstreamHref === undefined
643650 ? html`<span class="mono">${up}</span>`
644 : html`<a href="${upstreamHref}">${up}</a>`}
645 </dd>`,
651 : html`<a href="${upstreamHref}">${up}</a>`,
652 }),
646653 )}
647 </dl>
654 </ul>
648655 ${whereItLives}
649656 ${when(
650657 repo.visibility === "public" && repo.origin === null,
@@ -679,8 +686,7 @@ function aboutCard(
679686 >`,
680687 )}`,
681688 { head: html`<h2>About</h2>` },
682 )}
683 </aside>`;
689 )}`;
684690 }
685691
686692 // ---- pages ----
@@ -763,7 +769,7 @@ export async function renderCode(
763769 ? main
764770 : html`<div class="repo-layout">
765771 <div>${main}</div>
766 ${o.rail}
772 <aside aria-label="About">${o.rail}</aside>
767773 </div>`;
768774 }
769775
src/web/style/content.ts +1 −4
@@ -226,11 +226,8 @@ a.origin-chip:hover { border-color: var(--border-strong); color: var(--fg); }
226226 .repo-layout { display: grid; gap: 16px; margin-top: 16px; align-items: start; }
227227 @media (min-width: 960px) {
228228 .repo-layout { grid-template-columns: minmax(0, 1fr) 280px; gap: 24px; }
229 .repo-layout > .about { position: sticky; top: calc(var(--topbar-h) + 16px); }
229 .repo-layout > aside { position: sticky; top: calc(var(--topbar-h) + 16px); }
230230 }
231 .about dl { display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; font-size: 13px; margin: 0; }
232 .about dt { color: var(--muted-fg); }
233 .about dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
234231
outdated

Stale comment from the retired make-it-yours rule; it describes nothing here. Remove it together with .kv and use row/.setting-list.

235232 .issue-head { margin: 20px 0 14px; }
outdated

.kv was retired in #3896 in favour of the §5 row component. Re-adding a bespoke grid is hand-rolled markup the design doc forbids; use row instead.

236233 .issue-head h1 { font-size: clamp(20px, 1rem + 1.4vw, 26px); letter-spacing: -0.025em; }
test/web-browse.test.ts +5
@@ -260,6 +260,11 @@ describe("browsing an origin repository on the web", () => {
260260 expect(html).toContain(new URL(fake.url).host);
261261 expect(html).toContain("read-only");
262262 expect(html).toContain("Mirror it to");
263 // The About rail is a §5 card, not the retired `.about` aside.
264 expect(html).toContain("Where it lives");
265 expect(html).not.toMatch(/class="about"/);
266 expect(html).toContain('<aside aria-label="About">');
267 expect(html).toMatch(/row-title"[^>]*>Visibility</);
263268 expect(html).toContain("keep it in sync");
264269 // And the two things it offers instead of the write buttons.
265270 expect(html).toContain("Mirror into your workbench");
test/web-origins.test.ts +5
@@ -364,6 +364,11 @@ describe("origins on the web", () => {
364364 expect(html).toContain("Last fetch");
365365 expect(html).toContain("Sync now");
366366 expect(html).toContain("Pushes here go through to");
367 expect(html).not.toMatch(/class="about"/);
368 // The facts render as §5 rows inside a labelled complementary landmark.
369 expect(html).toContain('<aside aria-label="About">');
370 expect(html).toMatch(/row-title"[^>]*>Visibility</);
371 expect(html).not.toContain("<dl");
367372 });
368373
369374 test("an imported issue shows the origin's author, not the local one", async () => {

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs