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

The page head is a §5 component: `.page-head` moves from §5d into the table, with the function that draws it #4840

Merged opened this

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

Merged

mason/forge:mason/4832-the-page-head-is-a-5-component-page-head → main 7 commits fork

  • Merged. mason/4832-the-page-head-is-a-5-component-page-head was merged into main as a7ed99d.

Review

Sign in to review this change.
7 files changed +43 −15
docs/design/areas/layout.md +1 −1
@@ -10,4 +10,4 @@ The spacing and layout utilities every page leans on: one component, styled in `
1010 - `.grow` — a flex child that takes the free space and may shrink below its content (`min-width: 0`), so its text can truncate; `pages/edit.ts`, `pages/settings.ts` and `pages/board.ts` write it. Styled in `style/base.ts`; `style/components.ts` ellipsizes it inside `.menu-item` and `.palette-item`.
1111 - `.narrow` — a 400px column centred a little below the top, for a single short form or notice; `pages/oauth.ts` writes it. Styled in `style/base.ts`.
1212 - `.narrow-wide` — a 760px reading column for a page of one form or one text; `pages/edit.ts`, `pages/issues.ts` and `pages/forges.ts` write it. Styled in `style/base.ts`.
13 - `.page-head-sub` — the one line under a page head's title: `flex-basis: 100%` and a −4px top margin, so it breaks onto its own full-width line. A layout hook, not a look: it rides with `muted small` (or `bar muted small` for a row of facts); `pageHead({ sub })` in `chrome.ts` and the run, tell, change and browse heads write it. Styled in `style/content.ts`; a `.muted.small` child of a page head without it (the system page's Processes sort row) stays beside its heading.
13 - `.page-head-sub` — the one line under a page head's title: `flex-basis: 100%` and a −4px top margin, so it breaks onto its own full-width line. A layout hook, not a look: it rides with `muted small` (or `bar muted small` for a row of facts); `pageHead({ sub })` in `chrome.ts` and the run, tell, change and browse heads write it. Styled in `style/components.ts`; a `.muted.small` child of a page head without it (the system page's Processes sort row) stays beside its heading.
docs/design/sections/5-components.md +1
@@ -38,6 +38,7 @@ specification.
3838 | **tooltip** | `[data-tip]` | `data-tip-side=bottom` | hover/focus; hidden on coarse pointers | a status-bar hint |
3939 | **toast / flash** `Flash` | `.toast` | `data-tone=success\|danger\|attn` | one per page load, cookie-driven | a one-line banner |
4040 | **command palette** | `.palette` | — | JS only; degrades to `/search` | a fuzzy-find overlay |
41 | **page head** `pageHead()` (`chrome.ts`) | `.page-head` (`h1`, `.page-head-sub`; a `.spacer` before the actions) | `lead` before the title, `actions` right-aligned, `sub` on its own line below | wraps; the title's `min-width: 0` lets a long one shrink beside the actions | the title line, actions right, a dim subtitle under it |
4142 | **breadcrumb** `crumbs()` | `.crumbs` | `.mono` | `aria-current` on the last | `a / b / c` |
4243 | **pagination** `pager()` | `.pager` | — | cursor links, absent ends | `‹ Newer Page 2 Older ›` |
4344 | **timeline item** `tlEvent()` / `tlComment()` | `.tl-item` | `data-kind=comment\|event`, `data-tone` | — | a card, or one dim line with a glyph |
docs/design/sections/5d-page-local-classes.md +1 −2
@@ -27,8 +27,7 @@ the same line (#4888).
2727 - `.menu-item`
2828 - `.preview`
2929 - `.swatch`
30 - **`style/content.ts`, page furniture** — what frames a page's content: page and repo heads, the front page and its tiles, explore, backups, boards, the fleet chart's series colours, lineage:
31 - `.page-head`
30 - **`style/content.ts`, page furniture** — what frames a page's content: repo heads, the front page and its tiles, explore, backups, boards, the fleet chart's series colours, lineage:
3231 - `.repo-actions`
3332 - `.repo-desc`
3433 - **`style/content.ts`, apps, runs, and the crew** — run pages, their logs and checks (a pull's look is §5's **look**).
src/web/style/components.ts +8
@@ -763,6 +763,14 @@ details[open] > summary.row > .row-lead { transform: rotate(90deg); }
763763 [data-tip]:hover::after, [data-tip]:focus-visible::after { opacity: 1; }
764764 @media (pointer: coarse) { [data-tip]::after { display: none; } }
765765
766 /* ---- page head: title, actions, subtitle ---- */
767 .page-head {
768 display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
769 margin: 20px 0 14px;
770 }
771 .page-head h1 { min-width: 0; }
772 .page-head > .page-head-sub { flex-basis: 100%; margin-top: -4px; }
773
766774 /* ---- breadcrumbs ---- */
767775 .crumbs {
768776 display: flex; align-items: center; gap: 4px; min-width: 0;
src/web/style/content.ts +1 −8
@@ -1,6 +1,6 @@
11 /**
22 * What pages put inside the components: rendered prose, a file's source, and
3 * the diff — plus the page furniture that frames them (page head, repo
3 * the diff — plus the page furniture that frames them (repo
44 * header, merge box, file tree). The diff is the product, so it gets the most
55 * attention here: hairline gutters, an opaque sticky file header, a `+`/`−`
66 * glyph that survives copy-paste, and an intra-line change signalled by two
@@ -11,13 +11,6 @@ export const content = /* css */ `
1111 @layer content {
1212
1313 /* ---- page furniture ---- */
14 .page-head {
15 display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
16 margin: 20px 0 14px;
17 }
18 .page-head h1 { min-width: 0; }
19 .page-head > .page-head-sub { flex-basis: 100%; margin-top: -4px; }
20
2114 .repo-head { margin: 18px 0 0; view-transition-name: repo-head; }
2215 .repo-title {
2316 display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
test/page-head-component.test.ts added +27
@@ -0,0 +1,27 @@
1 /** #4832: the page head is a §5 component — its rules live once, in the components layer. */
2 import { expect, test } from "bun:test";
3 import { readFileSync } from "node:fs";
4 import { pageHead } from "../src/web/chrome.ts";
5 import { css } from "../src/web/style.ts";
6 import { components } from "../src/web/style/components.ts";
7 import { content } from "../src/web/style/content.ts";
8
9 const rules = (sheet: string) => [...sheet.matchAll(/^\.page-head\b[^{]*\{/gm)].map((m) => m[0]);
10
11 test("the served stylesheet defines .page-head once, in style/components.ts", () => {
12 expect(rules(css)).toEqual([".page-head {", ".page-head h1 {", ".page-head > .page-head-sub {"]);
13 expect(rules(components)).toEqual(rules(css));
14 expect(rules(content)).toEqual([]);
15 });
16
17 test("§5's table names pageHead() as the page head", () => {
18 const doc = readFileSync("docs/design/sections/5-components.md", "utf8");
19 expect(doc).toMatch(/^\| \*\*page head\*\* `pageHead\(\)` \(`chrome\.ts`\) +\| `\.page-head`/m);
20 });
21
22 test("pageHead() draws the class and its parts", () => {
23 const out = String(pageHead("Title", { sub: "Below" }));
24 expect(out).toContain('<div class="page-head">');
25 expect(out).toContain("<h1>Title</h1>");
26 expect(out).toContain('<span class="page-head-sub muted small">Below</span>');
27 });
test/page-head-sub.test.ts +4 −4
@@ -11,10 +11,10 @@ test("a page head's subtitle is the shared muted, small text, not a page-local c
1111 });
1212
1313 test("only the subtitle is forced onto its own line: the stylesheet keys on .page-head-sub, not on shared utilities", async () => {
14 const { content } = await import("../src/web/style/content.ts");
15 expect(content).toContain(".page-head > .page-head-sub {");
16 expect(content).not.toContain(".page-head > .muted.small");
17 expect(content).not.toContain(".page-head > .bar");
14 const { components } = await import("../src/web/style/components.ts");
15 expect(components).toContain(".page-head > .page-head-sub {");
16 expect(components).not.toContain(".page-head > .muted.small");
17 expect(components).not.toContain(".page-head > .bar");
1818 // The system page's sort row is a plain muted small child, beside its heading.
1919 const src = await Bun.file(new URL("../src/web/pages/system.ts", import.meta.url)).text();
2020 expect(src).not.toContain("page-head-sub");

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs