47 lines
1.3 KiB
TypeScript
47 lines
1.3 KiB
TypeScript
import type { ReactNode } from 'react';
|
|
|
|
/** How long a success state lingers before the wizard auto-advances, in ms. */
|
|
export const SUCCESS_DWELL_MS = 2000;
|
|
|
|
interface OnboardingStepProps {
|
|
title: ReactNode;
|
|
subtitle?: ReactNode;
|
|
/** The interactive body (keyboard diagram, toggle, etc.). */
|
|
children?: ReactNode;
|
|
/** Live feedback line, announced to screen readers. */
|
|
status?: ReactNode;
|
|
/** Actions and hints below the body. */
|
|
footer?: ReactNode;
|
|
}
|
|
|
|
/** Shared layout so every onboarding step keeps the same vertical rhythm. */
|
|
export function OnboardingStep({
|
|
title,
|
|
subtitle,
|
|
children,
|
|
status,
|
|
footer,
|
|
}: OnboardingStepProps) {
|
|
return (
|
|
<div className="flex flex-col items-center gap-6 text-center">
|
|
<div className="space-y-2">
|
|
<h2 className="text-2xl font-semibold tracking-tight text-white">
|
|
{title}
|
|
</h2>
|
|
{subtitle && (
|
|
<p className="mx-auto max-w-xs text-sm text-white/50">{subtitle}</p>
|
|
)}
|
|
</div>
|
|
{children && (
|
|
<div className="flex flex-col items-center gap-4 py-2">{children}</div>
|
|
)}
|
|
<p aria-live="polite" className="min-h-5 text-sm text-white/60">
|
|
{status}
|
|
</p>
|
|
{footer && (
|
|
<div className="flex flex-col items-center gap-3">{footer}</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|