style(notifications): the Earlier fold reads as a section heading, like its siblings

It was a ghost button in body type under two uppercase section labels. It is now
the heading itself, its text the disclosure's button (24px tall, past the
WCAG 2.5.8 floor).
This commit is contained in:
2026-10-28 20:42:18 +01:00
parent f49dc6532f
commit a5666bfde9
2 changed files with 19 additions and 10 deletions
@@ -139,6 +139,8 @@ describe("NotificationsPanel · order", () => {
expect(rows()).toEqual(["Ready 1"]); expect(rows()).toEqual(["Ready 1"]);
const fold = screen.getByRole("button", { name: "inbox.earlier:2" }); const fold = screen.getByRole("button", { name: "inbox.earlier:2" });
expect(fold.getAttribute("aria-expanded")).toBe("false"); expect(fold.getAttribute("aria-expanded")).toBe("false");
// A section heading like "Needs you" and "Recent", its text the disclosure's button.
expect(fold.closest("h2")).not.toBeNull();
fireEvent.click(fold); fireEvent.click(fold);
expect(fold.getAttribute("aria-expanded")).toBe("true"); expect(fold.getAttribute("aria-expanded")).toBe("true");
expect(rows()).toEqual(["Ready 1", "Activity 3", "Ready 2"]); expect(rows()).toEqual(["Ready 1", "Activity 3", "Ready 2"]);
@@ -46,6 +46,7 @@ import { channelYouTubeUrl, formatDate } from "../../lib/format";
import { entryKey, keyOf, orderInbox, type InboxEntry } from "../../lib/inboxOrder"; import { entryKey, keyOf, orderInbox, type InboxEntry } from "../../lib/inboxOrder";
import { Button } from "../../components/ui/Button"; import { Button } from "../../components/ui/Button";
import { IconButton } from "../../components/ui/IconButton"; import { IconButton } from "../../components/ui/IconButton";
import { Pressable } from "../../components/ui/Pressable";
import { LEVEL_STYLE } from "../../components/ui/toastLevels"; import { LEVEL_STYLE } from "../../components/ui/toastLevels";
import { PageToolbar } from "../../components/ui/PageShell"; import { PageToolbar } from "../../components/ui/PageShell";
import { PageMenu } from "../../components/ui/PageMenu"; import { PageMenu } from "../../components/ui/PageMenu";
@@ -481,16 +482,22 @@ export default function NotificationsPanel() {
)} )}
{order.earlier.length > 0 && ( {order.earlier.length > 0 && (
<section> <section>
<Button {/* A heading like its siblings, its text the disclosure's button (the ARIA
variant="ghost" disclosure pattern); 24px tall so the target clears the WCAG 2.5.8 floor. */}
size="sm" <SectionLabel className={earlierOpen ? "mb-2" : undefined}>
aria-expanded={earlierOpen} <Pressable
onClick={() => setEarlierOpen((o) => !o)} aria-expanded={earlierOpen}
icon={earlierOpen ? <ChevronDown /> : <ChevronRight />} onClick={() => setEarlierOpen((o) => !o)}
className={earlierOpen ? "mb-2" : undefined} className="min-h-6 inline-flex items-center gap-1 uppercase hover:text-fg transition"
> >
{t("inbox.earlier", { count: order.earlier.length })} {earlierOpen ? (
</Button> <ChevronDown className="size-3.5" aria-hidden />
) : (
<ChevronRight className="size-3.5" aria-hidden />
)}
{t("inbox.earlier", { count: order.earlier.length })}
</Pressable>
</SectionLabel>
{earlierOpen && ( {earlierOpen && (
<NavGrid className="flex flex-col gap-2">{order.earlier.map(entry)}</NavGrid> <NavGrid className="flex flex-col gap-2">{order.earlier.map(entry)}</NavGrid>
)} )}