commit 2907e08946920fb94b916d7d366a250c65208fc7
parent 14ce4c3d69204dcf226174318058a369df646157
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Fri, 25 Sep 2026 21:30:59 -0400
homepage: the header is the parent mark + Archi|lyzer, no longer tracked uppercase (brand S1)
The CSS triangle becomes the bone-on-slate mark and "Archilyzer" the split
wordmark. The link keeps aria-label "Archilyzer home". brand.spec pins the
header and the rendered icon set.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
2 files changed, 55 insertions(+), 10 deletions(-)
diff --git a/homepage/app/components/Header.tsx b/homepage/app/components/Header.tsx
@@ -1,5 +1,11 @@
import Link from "next/link";
-import { PROJECT_NAME } from "yt-dlp-transcript-common/lib/project";
+import {
+ PROJECT_NAME,
+ PROJECT_WORDMARK_LEAD,
+} from "yt-dlp-transcript-common/lib/project";
+import { ICON_PALETTES } from "yt-dlp-transcript-common/lib/brand";
+import { BrandMark } from "yt-dlp-transcript-common/components/BrandMark";
+import { Wordmark } from "yt-dlp-transcript-common/components/Wordmark";
import { ThemeToggle } from "yt-dlp-transcript-common/components/ThemeToggle";
import { ThemeMenu } from "yt-dlp-transcript-common/components/ThemeMenu";
import { NAV } from "../lib/nav";
@@ -30,24 +36,25 @@ function NavList({ className }: { className?: string }) {
// is the same string on every install. The operator's own naming still governs
// /stats/ and the social links in the footer.
//
-// The mark is a solid triangle — a play head, the thing every recording here
-// began as. Bone, not brass: this family spends no colour on chrome.
+// The mark is the family's Found-line mark in its parent colours — bone on
+// slate, no accent: the project spends no colour on its own chrome. The
+// wordmark splits on the subject ("Archi" + "lyzer"); the link keeps its
+// explicit name, "Archilyzer home".
export default function Header() {
return (
<header className="sticky top-0 z-20 border-b border-[var(--border)] bg-[var(--background)]/85 backdrop-blur-md">
<div className="max-w-6xl mx-auto px-5 sm:px-6 flex h-14 items-center gap-6">
<Link
href="/"
- className="group flex items-center gap-2.5 shrink-0"
+ className="flex items-center gap-2.5 shrink-0"
aria-label={`${PROJECT_NAME} home`}
>
- <span
- aria-hidden="true"
- className="h-0 w-0 border-y-[6px] border-y-transparent border-l-[9px] border-l-[var(--foreground)] transition-colors group-hover:border-l-[var(--brand)]"
+ <BrandMark palette={ICON_PALETTES.archilyzer} className="size-7 shrink-0" />
+ <Wordmark
+ title={PROJECT_NAME}
+ lead={PROJECT_WORDMARK_LEAD}
+ className="text-[1.3rem] leading-none tracking-[-0.01em]"
/>
- <span className="font-display text-[1.05rem] font-semibold uppercase leading-none tracking-[0.11em] text-[var(--foreground)]">
- {PROJECT_NAME}
- </span>
</Link>
<nav aria-label="Main" className="ml-auto hidden sm:block">
<NavList className="gap-6" />
diff --git a/homepage/e2e/brand.spec.ts b/homepage/e2e/brand.spec.ts
@@ -0,0 +1,38 @@
+import { expect, test } from "@playwright/test";
+import { ICON_PALETTES, markSvg } from "../../common/lib/brand";
+
+// The project site wears the PARENT mark (plans/brand-and-themes.md, slice
+// S1): bone on slate in its icons and its header, and the wordmark split on the
+// subject — "Archi" heavy, "lyzer" light — no longer tracked uppercase.
+
+test("the header is the parent mark + Archi|lyzer, still named 'Archilyzer home'", async ({
+ page,
+}) => {
+ await page.goto("/");
+ const home = page.getByRole("link", { name: "Archilyzer home", exact: true });
+ await expect(home).toBeVisible();
+ await expect(home.locator("[data-wordmark-lead]")).toHaveText("Archi");
+ await expect(home.locator("[data-wordmark-suffix]")).toHaveText("lyzer");
+ await expect(home.locator("[data-wordmark]")).not.toHaveCSS("text-transform", "uppercase");
+ const mark = home.locator("svg[data-brand-mark]");
+ await expect(mark).toHaveAttribute("aria-hidden", "true");
+ const fills = await mark.evaluate((svg) =>
+ ["ground", "dim", "lit"].map(
+ (t) => getComputedStyle(svg.querySelector(`[data-tone="${t}"]`) as Element).fill,
+ ),
+ );
+ expect(fills).toEqual(["rgb(21, 27, 32)", "rgb(63, 76, 86)", "rgb(231, 237, 241)"]);
+});
+
+test("the icon set is rendered from the parent mark", async ({ request }) => {
+ const svg = await request.get("/icons/icon.svg");
+ expect(svg.headers()["content-type"]).toBe("image/svg+xml");
+ expect(await svg.text()).toBe(markSvg(ICON_PALETTES.archilyzer));
+ // The OG image names this one.
+ const png = await request.get("/icons/icon-512.png");
+ expect(png.headers()["content-type"]).toBe("image/png");
+ const b = await png.body();
+ expect([b.readUInt32BE(16), b.readUInt32BE(20)]).toEqual([512, 512]);
+ const ico = await request.get("/favicon.ico");
+ expect([...(await ico.body()).subarray(0, 4)]).toEqual([0, 0, 1, 0]);
+});