Files
HealthLog/src/components/onboarding/step-heading.tsx
T
Marc-André Bombeck 1c2e7c29de Add the setup flow's welcome screen and shell
The front door reads the same ledger every screen reads: a record that
never entered the flow sees the welcome, one mid-way is sent to the step
it owes, a finished one is offered the questions again. The shell prints
the counter and an ordered step list off the step machine, so it shows
exactly the screens this flow has, and every screen moves focus to its
heading on arrival.

"Skip for now" is a real exit — it stamps the account past the first-run
redirect and opens the checklist on the dashboard — and the one-time
medical disclaimer now gates it as well as "Set up", because a skip that
reached the dashboard without it would be a way round the acknowledgment.

The five-step wizard's welcome carousel, goal picker and source grid go
with it; the numbered step route follows in the next commit.
2026-09-10 15:10:50 +02:00

51 lines
1.3 KiB
TypeScript

"use client";
import { useEffect, useRef, type ReactNode } from "react";
/**
* The `h1` of every setup screen, and the one place focus goes when a screen
* arrives.
*
* Moving focus to the heading on each step is what lets a screen-reader user
* follow the flow: without it, a client-side navigation leaves focus on the
* button that was just pressed, which no longer exists, and the new question
* is never announced. `preventScroll` keeps the viewport where the layout
* put it — the heading is at the top of the column anyway.
*
* Semibold rather than the app-wide bold PageHeader H1: the onboarding hero
* is the documented exception (UI-STANDARDS §5). Do not sweep to font-bold.
*/
export function StepHeading({
id,
title,
description,
}: {
id: string;
title: ReactNode;
description?: ReactNode;
}) {
const ref = useRef<HTMLHeadingElement | null>(null);
useEffect(() => {
ref.current?.focus({ preventScroll: true });
}, []);
return (
<header className="space-y-2">
<h1
ref={ref}
id={id}
tabIndex={-1}
className="text-2xl font-semibold tracking-tight outline-none"
>
{title}
</h1>
{description ? (
<p className="text-muted-foreground text-base leading-relaxed">
{description}
</p>
) : null}
</header>
);
}