Files
siftlode/frontend/src/lib/headings.contract.test.ts
T
peter eddeca82d3 feat(design): the page band (B4) — one h1 per page, section headings, skip to content, filters fold on a short window (E28 S3d4)
- PageShell names the page with a visually hidden h1; a view with its own title renders it as
  PageHeading and the hidden one steps aside (contract: no hand <h1> inside the shell).
- Section captions are SectionLabel headings (h2, h3 in a dialog), ui/form Section included.
- "Skip to content" is the first Tab stop and focuses the scroller's <main>.
- Below 600px of height the band's filter row folds into "Filters · n" (Feed, Plex, server log),
  the same row shown in place.
- The band holds controls only: Downloads and Notifications lose their title cards, the Config and
  channel-discovery intros scroll with the content, the Stats tabs and the audit pager join the band,
  and the server log's toolbar sticks with the fade under it.
2026-09-26 00:39:01 +02:00

40 lines
1.5 KiB
TypeScript

import { readdirSync, readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
// E28 S3d4 (B4, UX-15). One `h1` per page: the shell's hidden page name, or a view's own visible
// title through `PageHeading`, which makes the shell's step aside. A hand-written `<h1>` inside the
// shell would be a second one, beside the page name the shell already put there.
function sources(): Array<[string, string]> {
const walk = (u: URL, prefix: string): Array<[string, string]> =>
readdirSync(u, { withFileTypes: true }).flatMap((e) => {
if (e.isDirectory()) return walk(new URL(`${e.name}/`, u), `${prefix}${e.name}/`);
if (!/\.tsx$/.test(e.name) || e.name.includes(".test.")) return [];
return [
[`${prefix}${e.name}`, readFileSync(new URL(e.name, u), "utf-8")] as [string, string],
];
});
return walk(new URL("../", import.meta.url), "");
}
// Pages that render OUTSIDE the app shell own their h1 (no shell, no hidden page name).
const STANDALONE = new Set([
"components/Welcome.tsx",
"components/WatchPage.tsx",
"components/legal/LegalLayout.tsx",
"design-lab/DesignLab.tsx",
// The shell's hidden h1 and the primitive itself.
"components/ui/PageShell.tsx",
"components/ui/Headings.tsx",
]);
describe("the page heading", () => {
it("is written as PageHeading inside the shell, never as a hand <h1>", () => {
const own = sources()
.filter(([name]) => !STANDALONE.has(name))
.filter(([, src]) => /<h1[\s>]/.test(src))
.map(([name]) => name);
expect(own).toEqual([]);
});
});