feat(colophon): add a Ko-fi link, and group the author's links together
The link sits last in the existing row beside @StereotypicalCat and Source code, in that row's own muted grey, with the same hover and the same small mark. No accent colour, no button, no sentence asking for anything, and the footer still contains no appeal anywhere — a test asserts that. This page earns its keep by being trusted about dates, and a tip jar competing with the disclaimer two paragraphs above spends that trust to make an ask. As one more link it costs nothing and is there for a reader already looking. The row also moves above "Additional ideas and design from". That row is who built this and where to find them; the credit below it is other people. Reading order now follows that split, instead of routing a reader past five strangers' handles to reach the author's own links. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6552528acb
commit
72d57dd02a
@@ -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
|
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)
|
scripts/ build-feed.ts, build-static.ts, parse-fixture.ts (offline), refresh-sources.ts (fetches)
|
||||||
serve.ts static server + /api/health
|
serve.ts static server + /api/health
|
||||||
test/ 730 tests
|
test/ 732 tests
|
||||||
fixtures/<game>/ raw HTML + .expected.json per source — pinned, kept forever
|
fixtures/<game>/ raw HTML + .expected.json per source — pinned, kept forever
|
||||||
snapshots/ current page per source, rewritten by refresh — see its README
|
snapshots/ current page per source, rewritten by refresh — see its README
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export const AUTHOR = {
|
|||||||
name: "Lucas Winther",
|
name: "Lucas Winther",
|
||||||
site: "https://lucaswinther.info",
|
site: "https://lucaswinther.info",
|
||||||
github: "https://github.com/StereotypicalCat",
|
github: "https://github.com/StereotypicalCat",
|
||||||
|
kofi: "https://ko-fi.com/stereotypicalcat",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const LINK =
|
const LINK =
|
||||||
@@ -98,6 +99,15 @@ function GitHubMark() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function KofiMark() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 16 16" className="size-3.5" fill="currentColor" aria-hidden>
|
||||||
|
<path d="M2 3h9.2a3.3 3.3 0 0 1 0 6.6h-.6A4 4 0 0 1 6.7 13H5.3a4 4 0 0 1-4-4V3.7c0-.4.3-.7.7-.7Zm8.6 5.3h.6a2 2 0 0 0 0-4h-.6v4Z" />
|
||||||
|
<path d="M4.4 5.6c.5-.5 1.2-.4 1.6 0 .4-.4 1.1-.5 1.6 0 .5.5.4 1.2 0 1.7L6 8.9 4.4 7.3c-.4-.5-.5-1.2 0-1.7Z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** Display name and homepage for a source host. */
|
/** Display name and homepage for a source host. */
|
||||||
const SITES: Record<string, { name: string; url: string }> = {
|
const SITES: Record<string, { name: string; url: string }> = {
|
||||||
"game8.co": { name: "Game8", url: "https://game8.co" },
|
"game8.co": { name: "Game8", url: "https://game8.co" },
|
||||||
@@ -288,26 +298,6 @@ export function Colophon({
|
|||||||
{"."}
|
{"."}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{IDEA_CREDITS.length > 0 && (
|
|
||||||
<p className="mt-2">
|
|
||||||
Additional ideas and design from{" "}
|
|
||||||
{IDEA_CREDITS.map((c, i) => (
|
|
||||||
<span key={c.handle}>
|
|
||||||
{i > 0 && (i === IDEA_CREDITS.length - 1 ? " and " : ", ")}
|
|
||||||
<a
|
|
||||||
href={c.url}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer noopener"
|
|
||||||
className={LINK}
|
|
||||||
>
|
|
||||||
{c.handle}
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
{"."}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<p className="mt-2 flex flex-wrap items-center gap-x-4 gap-y-1.5">
|
<p className="mt-2 flex flex-wrap items-center gap-x-4 gap-y-1.5">
|
||||||
<a
|
<a
|
||||||
href={AUTHOR.github}
|
href={AUTHOR.github}
|
||||||
@@ -327,8 +317,45 @@ export function Colophon({
|
|||||||
<GitHubMark />
|
<GitHubMark />
|
||||||
Source code
|
Source code
|
||||||
</a>
|
</a>
|
||||||
|
{/*
|
||||||
|
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.
|
||||||
|
*/}
|
||||||
|
<a
|
||||||
|
href={AUTHOR.kofi}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer noopener me"
|
||||||
|
className="inline-flex items-center gap-1.5 text-muted transition-colors duration-150 hover:text-ink"
|
||||||
|
>
|
||||||
|
<KofiMark />
|
||||||
|
Ko-fi
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
{IDEA_CREDITS.length > 0 && (
|
||||||
|
<p className="mt-2">
|
||||||
|
Additional ideas and design from{" "}
|
||||||
|
{IDEA_CREDITS.map((c, i) => (
|
||||||
|
<span key={c.handle}>
|
||||||
|
{i > 0 && (i === IDEA_CREDITS.length - 1 ? " and " : ", ")}
|
||||||
|
<a
|
||||||
|
href={c.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer noopener"
|
||||||
|
className={LINK}
|
||||||
|
>
|
||||||
|
{c.handle}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
{"."}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
{/*
|
{/*
|
||||||
Placed under the disclaimer that admits dates can be wrong, because that
|
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
|
paragraph is where a reader who has just found one is looking. The bug
|
||||||
|
|||||||
+25
-1
@@ -3,7 +3,7 @@ import { renderToStaticMarkup } from "react-dom/server";
|
|||||||
import { EventForm } from "../src/client/components/CustomForms.tsx";
|
import { EventForm } from "../src/client/components/CustomForms.tsx";
|
||||||
import { YourOwn } from "../src/client/components/YourOwn.tsx";
|
import { YourOwn } from "../src/client/components/YourOwn.tsx";
|
||||||
import { EventRow } from "../src/client/components/EventRow.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 { GameMetaProvider } from "../src/client/state/gameMeta.tsx";
|
||||||
import {
|
import {
|
||||||
asDisplayEvent,
|
asDisplayEvent,
|
||||||
@@ -280,4 +280,28 @@ describe("Colophon freshness notice (PRD F7)", () => {
|
|||||||
);
|
);
|
||||||
expect(html).toContain("Reverse: 1999 (never)");
|
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(<Colophon sources={[fresh]} now={NOW} />);
|
||||||
|
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(<Colophon sources={[fresh]} now={NOW} />);
|
||||||
|
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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user