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.
This commit is contained in:
@@ -202,28 +202,27 @@ export default function ChannelDiscovery({
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Intro + the table controls are fixed chrome; only the table rows scroll (sticky header). */}
|
||||
{/* The table controls are the page's band (B4); only the rows scroll (sticky header). The intro
|
||||
scrolls with them: in the band it held two lines of a short window for text read once. */}
|
||||
<PageToolbar>
|
||||
<div className="px-4 pt-2 max-w-384 mx-auto flex items-start justify-between gap-4">
|
||||
<p className="text-xs text-muted leading-relaxed">{t("channels.discovery.intro")}</p>
|
||||
{/* Cards has no column headers, so a dropdown drives the sort + a pager (table uses its own). */}
|
||||
{layout === "cards" && (
|
||||
<div className="flex items-center gap-3 shrink-0">
|
||||
<Pager {...pagerProps} />
|
||||
<ColumnSortControl
|
||||
columns={columns}
|
||||
sort={sort}
|
||||
onChange={setSort}
|
||||
label={t("channels.sort.label")}
|
||||
defaultLabel={t("channels.sort.default")}
|
||||
ascLabel={t("channels.sort.asc")}
|
||||
descLabel={t("channels.sort.desc")}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* Cards has no column headers, so a dropdown drives the sort + a pager (table uses its own). */}
|
||||
{layout === "cards" && (
|
||||
<div className="px-4 pt-2 max-w-384 mx-auto flex items-center justify-end gap-3">
|
||||
<Pager {...pagerProps} />
|
||||
<ColumnSortControl
|
||||
columns={columns}
|
||||
sort={sort}
|
||||
onChange={setSort}
|
||||
label={t("channels.sort.label")}
|
||||
defaultLabel={t("channels.sort.default")}
|
||||
ascLabel={t("channels.sort.asc")}
|
||||
descLabel={t("channels.sort.desc")}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</PageToolbar>
|
||||
<div className="px-4 pb-4 max-w-384 mx-auto">
|
||||
<p className="text-xs text-muted leading-relaxed pt-2">{t("channels.discovery.intro")}</p>
|
||||
{query.isError && !query.data ? (
|
||||
<StateMessage tone="error" onRetry={() => query.refetch()}>
|
||||
{t("common.loadError")}
|
||||
|
||||
Reference in New Issue
Block a user