diff --git a/AGENTS.md b/AGENTS.md index 62d2119..da5fb11 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -119,7 +119,7 @@ src/client/ React app, service worker, manifest theme.ts — dark or light, and what a game hue reads as on each scripts/ build-feed.ts, build-static.ts, parse-fixture.ts (offline), refresh-sources.ts (fetches) serve.ts static server + /api/health -test/ 730 tests +test/ 732 tests fixtures// raw HTML + .expected.json per source — pinned, kept forever snapshots/ current page per source, rewritten by refresh — see its README ``` diff --git a/src/client/components/Colophon.tsx b/src/client/components/Colophon.tsx index 59df791..458f2f2 100644 --- a/src/client/components/Colophon.tsx +++ b/src/client/components/Colophon.tsx @@ -37,6 +37,7 @@ export const AUTHOR = { name: "Lucas Winther", site: "https://lucaswinther.info", github: "https://github.com/StereotypicalCat", + kofi: "https://ko-fi.com/stereotypicalcat", } as const; const LINK = @@ -98,6 +99,15 @@ function GitHubMark() { ); } +function KofiMark() { + return ( + + + + + ); +} + /** Display name and homepage for a source host. */ const SITES: Record = { "game8.co": { name: "Game8", url: "https://game8.co" }, @@ -288,26 +298,6 @@ export function Colophon({ {"."}

- {IDEA_CREDITS.length > 0 && ( -

- Additional ideas and design from{" "} - {IDEA_CREDITS.map((c, i) => ( - - {i > 0 && (i === IDEA_CREDITS.length - 1 ? " and " : ", ")} - - {c.handle} - - - ))} - {"."} -

- )} -

Source code + {/* + Last in the row of small links, in the row's own muted grey, with no + accent colour, no button and no sentence asking for anything. This + page's job is to be trusted about dates, and a tip jar that competes + with the disclaimer above it spends that trust to make an ask — so it + reads as one more link for a reader already looking at the footer, and + is invisible to everyone else. + */} + + + Ko-fi +

+ {IDEA_CREDITS.length > 0 && ( +

+ Additional ideas and design from{" "} + {IDEA_CREDITS.map((c, i) => ( + + {i > 0 && (i === IDEA_CREDITS.length - 1 ? " and " : ", ")} + + {c.handle} + + + ))} + {"."} +

+ )} + {/* Placed under the disclaimer that admits dates can be wrong, because that paragraph is where a reader who has just found one is looking. The bug diff --git a/test/custom-ui.test.tsx b/test/custom-ui.test.tsx index b941955..32a5a4b 100644 --- a/test/custom-ui.test.tsx +++ b/test/custom-ui.test.tsx @@ -3,7 +3,7 @@ import { renderToStaticMarkup } from "react-dom/server"; import { EventForm } from "../src/client/components/CustomForms.tsx"; import { YourOwn } from "../src/client/components/YourOwn.tsx"; import { EventRow } from "../src/client/components/EventRow.tsx"; -import { Colophon } from "../src/client/components/Colophon.tsx"; +import { AUTHOR, Colophon, REPO_URL } from "../src/client/components/Colophon.tsx"; import { GameMetaProvider } from "../src/client/state/gameMeta.tsx"; import { asDisplayEvent, @@ -280,4 +280,28 @@ describe("Colophon freshness notice (PRD F7)", () => { ); expect(html).toContain("Reverse: 1999 (never)"); }); + + test("the author's links sit together, above the ideas credit", () => { + // The row is who built this and where to find them; the credit below it is + // other people. Reading order follows that, so a reader scanning the footer + // does not pass a stranger's handle on the way to the author's own links. + const html = renderToStaticMarkup(); + const links = html.indexOf("@StereotypicalCat"); + const ideas = html.indexOf("Additional ideas"); + expect(links).toBeGreaterThan(-1); + expect(ideas).toBeGreaterThan(-1); + expect(links).toBeLessThan(ideas); + }); + + test("carries the Ko-fi link, in the row and not as an appeal", () => { + const html = renderToStaticMarkup(); + expect(html).toContain(AUTHOR.kofi); + // Last in the author's row, so it never lands between the disclaimer and + // the reader — and never louder than the links beside it. + expect(html.indexOf(AUTHOR.kofi)).toBeGreaterThan(html.indexOf(REPO_URL)); + expect(html.indexOf(AUTHOR.kofi)).toBeLessThan(html.indexOf("Additional ideas")); + // Unobtrusive is a property of the markup, not a matter of taste: the same + // muted class as its neighbours, and no ask anywhere in the footer. + expect(html).not.toMatch(/support me|buy me|donate|tip jar/i); + }); });