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

One design language: every page, card and word at the home page's standard #9535

Merged opened this in answer to

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

Merged

mason/forge:mason/9433-one-design-language-every-page-card-and → main 2 commits fork

  • Merged. mason/9433-one-design-language-every-page-card-and was merged into main as fb73b4f.

Review

Sign in to review this change.
4 files changed +11 −29
src/web/og/card.ts +4 −25
@@ -73,17 +73,6 @@ const TITLE_LINES = 2;
7373 const EM_BOLD = 0.68;
7474 const EM_MONO = 0.62;
7575
76 const KIND_COLOR: Record<CardKind, string> = {
77 repo: T.accent,
78 issue: T.success,
79 pull: T.merged,
80 run: T.primary,
81 app: T.attn,
82 map: T.accent,
83 audit: T.success,
84 page: T.primary,
85 };
86
8776 /** Break a title into at most `n` lines of `max` chars; the last is clamped. */
8877 function wrap(text: string, size: number, n: number, em: number, width = INNER): string[] {
8978 const max = Math.max(1, Math.floor(width / (size * em)));
@@ -173,8 +162,6 @@ function drawImages(images: unknown): string {
173162 }
174163
175164 export function renderCard(input: CardInput): string {
176 const kind: CardKind = CARD_KINDS.includes(input?.kind) ? input.kind : "page";
177 const color = KIND_COLOR[kind];
178165 const out: string[] = [];
179166
180167 out.push(
@@ -185,16 +172,8 @@ export function renderCard(input: CardInput): string {
185172 text(PAD + 60, PAD + 26, 30, T.fg, "forge", ` font-weight="700"`),
186173 );
187174
188 // A card about one thing is marked with its kind: a strip down the left
189 // edge and a badge in the header. The forge's own card is plain, as the
190 // home page is.
191 const plain = kind === "page";
192 if (!plain) {
193 out.push(
194 `<rect x="0" y="0" width="12" height="${CARD_HEIGHT}" fill="${color}"/>`,
195 text(CARD_WIDTH - PAD, PAD + 24, 24, color, kind.toUpperCase(), ` font-weight="700" text-anchor="end" letter-spacing="2"`),
196 );
197 }
175 // Every card is as plain as the home page: no kind badge, no edge strip,
176 // one background, one text colour. The kind picks the card, not its look.
198177
199178 const figure = drawFigure(input?.figure) || drawImages(input?.images);
200179 const column = figure ? COLUMN : INNER;
@@ -204,7 +183,7 @@ export function renderCard(input: CardInput): string {
204183 const tsize = figure ? 56 : 64;
205184 const title = wrap(input?.title ?? "", tsize, TITLE_LINES, EM_BOLD, column);
206185 const tagline = clean(input?.tagline);
207 const tight = plain ? ` letter-spacing="-1.5"` : "";
186 const tight = ` letter-spacing="-1.5"`;
208187 let y = tagline ? 230 : 270;
209188 for (const row of title) {
210189 out.push(text(PAD, y, tsize, T.fg, row, ` font-weight="700"${tight}`));
@@ -234,7 +213,7 @@ export function renderCard(input: CardInput): string {
234213 const stat = clean(input?.stat);
235214 if (stat) {
236215 out.push(
237 text(PAD, CARD_HEIGHT - 52, 32, plain ? T["muted-fg"] : color, fit(stat, 32, INNER, EM_MONO), ` font-family="${MONO}" font-weight="600"`),
216 text(PAD, CARD_HEIGHT - 52, 32, T["muted-fg"], fit(stat, 32, INNER, EM_MONO), ` font-family="${MONO}" font-weight="600"`),
238217 );
239218 }
240219 out.push(`</svg>`);
test/og-card.test.ts +3
@@ -25,6 +25,9 @@ describe("og card", () => {
2525 expect(svg).toContain('width="1200" height="630"');
2626 expect(svg).toContain("Fast forge");
2727 expect(svg).toContain("12 open");
28 // As plain as the home page: no kind label, no edge strip.
29 expect(svg).not.toContain(`>${kind.toUpperCase()}<`);
30 expect(svg).not.toContain(`width="12"`);
2831 });
2932 }
3033
test/og-designs.test.ts +2 −2
@@ -106,7 +106,7 @@ describe("the map's thumbnail", () => {
106106 const figure = svg.slice(svg.indexOf("<g>"), svg.indexOf("</g>"));
107107 expect(figure.match(/<rect /g)?.length).toBe(4);
108108 expect(figure.match(new RegExp(`<path [^>]*stroke="${T.accent}"`, "g"))?.length).toBe(2);
109 expect(svg).toContain(">MAP<");
109 expect(svg).not.toContain(">MAP<");
110110 });
111111
112112 test("with nothing linked, the loose issues are drawn, four rows at most", () => {
@@ -237,7 +237,7 @@ describe("the audit's findings", () => {
237237 const svg = renderCard(card);
238238 wellFormed(svg);
239239 expect(svg).toContain(">50%<");
240 expect(svg).toContain(">AUDIT<");
240 expect(svg).not.toContain(">AUDIT<");
241241 });
242242
243243 test("every app holding says so; no apps draws no ring", () => {
test/og-route.test.ts +2 −2
@@ -197,8 +197,8 @@ describe("og cards", () => {
197197 });
198198
199199 test("the card says what the thing is", async () => {
200 expect(await (await get("/og/issue/alice~shown~1.svg")).text()).toContain("ISSUE");
201 expect(await (await get("/og/pull/alice~shown~2.svg")).text()).toContain("PULL");
200 expect(await (await get("/og/issue/alice~shown~1.svg")).text()).not.toContain("ISSUE");
201 expect(await (await get("/og/pull/alice~shown~2.svg")).text()).not.toContain("PULL");
202202 expect(await (await get(`/og/run/alice~shown~${runId}.svg`)).text()).toContain("did a thing");
203203 });
204204

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs