(feat): initial stuff
All checks were successful
/ package (push) Successful in 1m6s

This commit is contained in:
Alois 2026-07-26 02:56:07 +02:00
commit 29098d1f61
Signed by: alois
SSH key fingerprint: SHA256:GBzT2DXvAuGV9XIV5W3WrzVpjU54FThmxHXdbz95J24
40 changed files with 3113 additions and 515 deletions

View file

@ -51,6 +51,8 @@ function Button({
return (
<ButtonPrimitive
data-slot="button"
data-variant={variant}
data-size={size}
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>

View file

@ -12,7 +12,7 @@ function Card({
data-slot="card"
data-size={size}
className={cn(
"group/card flex flex-col gap-4 overflow-hidden rounded-xl bg-card py-4 text-sm text-card-foreground border has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
"group/card flex flex-col gap-4 overflow-hidden rounded-xl border border-dotted bg-card/30 py-4 text-sm text-card-foreground has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
className,
)}
{...props}

View file

@ -14,6 +14,9 @@ import { Button } from "./button";
interface OnboardingStep {
id: string;
title: ReactNode;
description?: ReactNode;
logo?: ReactNode;
content: ReactNode;
defaultCanContinue?: boolean;
}
@ -51,6 +54,9 @@ function OnboardingFlow({
steps: OnboardingStep[];
onFinish: () => void | Promise<void>;
}) {
if (steps.length === 0) {
throw new Error("OnboardingFlow requires at least one step");
}
const [stepIndex, setStepIndex] = useState(0);
const [controls, setControls] = useState<OnboardingStepControls>(() =>
defaultControls(steps[0]),
@ -90,7 +96,7 @@ function OnboardingFlow({
useEffect(() => {
const heading = document.querySelector<HTMLElement>(
`[data-onboarding-step="${activeStep.id}"] h1`,
`[data-onboarding-step="${activeStep.id}"] [data-onboarding-title]`,
);
heading?.focus();
}, [activeStep.id]);
@ -102,8 +108,27 @@ function OnboardingFlow({
className="min-h-0 flex-1 overflow-y-auto overscroll-contain"
data-onboarding-step={activeStep.id}
>
<header className="mx-auto flex w-full max-w-6xl items-start gap-4 px-6 pt-12 pb-7 md:px-10 md:pt-16">
{activeStep.logo}
<div className="min-w-0 flex-1">
<h1
data-onboarding-title
className="font-heading text-3xl font-bold md:text-4xl"
tabIndex={-1}
>
{activeStep.title}
</h1>
{activeStep.description && (
<div className="mt-2 text-lg text-muted-foreground">
{activeStep.description}
</div>
)}
</div>
</header>
<ConfigureStepContext.Provider value={configure}>
{activeStep.content}
<div className="mx-auto w-full max-w-6xl px-6 pb-12 md:px-10">
{activeStep.content}
</div>
</ConfigureStepContext.Provider>
</main>
@ -111,6 +136,7 @@ function OnboardingFlow({
<div className="flex justify-center md:justify-end">
<Button
size="lg"
className="w-full md:w-auto"
disabled={!controls.canContinue || pending}
onClick={() => void advance()}
>
@ -119,7 +145,7 @@ function OnboardingFlow({
</div>
<nav
className="mt-4 flex items-center justify-center gap-2"
className="mt-4 hidden items-center justify-center gap-2 md:flex"
aria-label="Onboarding steps"
>
<Button

View file

@ -37,6 +37,8 @@ function Toggle({
return (
<TogglePrimitive
data-slot="toggle"
data-variant={variant}
data-size={size}
className={cn(toggleVariants({ variant, size, className }))}
{...props}
/>