- {HOMEPAGE_SPOT_PRICE}
+ {SPONSOR_SPOT_PRICE}
a month
@@ -259,23 +241,73 @@ export function SponsorSection() {
);
}
-/** Homepage: the companies sponsoring Paseo. Renders nothing until there is one. */
-export function SponsorsSection() {
- if (HOMEPAGE_SPONSORS.length === 0) return null;
+const SPONSORS_PLACEMENTS = {
+ page: {
+ title: "Sponsors",
+ section: "space-y-10",
+ heading: "text-3xl font-medium tracking-tight",
+ link: "",
+ logo: "h-14",
+ description: "mt-2 text-sm text-white/55",
+ button: "h-12 px-5 text-sm",
+ },
+ footer: {
+ title: "Sponsored by",
+ section: "space-y-5 border-t border-white/10 py-8",
+ heading: "text-sm text-extra-muted-foreground",
+ link: "opacity-85 transition-opacity hover:opacity-100",
+ logo: "h-10",
+ description: "mt-1.5 text-xs text-white/45",
+ button: "h-9 px-3.5 text-sm",
+ },
+} as const;
+
+interface SponsorsSectionProps {
+ /** `page` is a homepage section; `footer` is the band above other pages' footers. */
+ placement: keyof typeof SPONSORS_PLACEMENTS;
+}
+
+/**
+ * The companies sponsoring Paseo: one monochrome logo per sponsor and, while
+ * spots are open, a quiet invitation. Every page shows it once.
+ */
+export function SponsorsSection({ placement }: SponsorsSectionProps) {
+ const style = SPONSORS_PLACEMENTS[placement];
+ const open = openSpotCount();
return (
-
);
}
diff --git a/packages/website/src/data/sponsors.test.ts b/packages/website/src/data/sponsors.test.ts
new file mode 100644
index 0000000000..f586cb9557
--- /dev/null
+++ b/packages/website/src/data/sponsors.test.ts
@@ -0,0 +1,64 @@
+import { existsSync, readFileSync } from "node:fs";
+import { fileURLToPath } from "node:url";
+import { describe, expect, it } from "vitest";
+import { SPONSORS, sponsorHref } from "./sponsors";
+
+const REPO_ROOT = fileURLToPath(new URL("../../../../", import.meta.url));
+const WEBSITE_PUBLIC = fileURLToPath(new URL("../../public", import.meta.url));
+const READMES = ["README.md", "README.zh-CN.md", "README.ja.md", "README.ko.md"];
+const README_SPONSOR_CELLS = 3;
+const SPONSOR_PAGE_URL = "https://paseo.sh/sponsor";
+
+describe("sponsorHref", () => {
+ it("tags the link with the placement it is shown in", () => {
+ const sponsor = {
+ name: "Acme",
+ url: "https://acme.dev",
+ logo: { onDark: "", onLight: "" },
+ description: "",
+ };
+
+ expect(sponsorHref(sponsor, "website")).toBe(
+ "https://acme.dev/?utm_source=paseo&utm_medium=website&utm_campaign=sponsor",
+ );
+ expect(sponsorHref(sponsor, "x")).toBe(
+ "https://acme.dev/?utm_source=paseo&utm_medium=x&utm_campaign=sponsor",
+ );
+ });
+});
+
+function readmeSponsorCells(readme: string) {
+ const text = readFileSync(`${REPO_ROOT}${readme}`, "utf8");
+ const table = text.slice(text.indexOf("