Retour au catalogue

Onboarding Checklist

Checklist d'onboarding avec barre de progression, items cochables et animation de completion.

onboardingmedium Both Responsive a11y
minimalplayfulsaasuniversalstacked
Theme
"use client";

import { useState } from "react";
import { Check, Circle, PartyPopper } from "lucide-react";

interface ChecklistItem {
  id: string;
  label: string;
  description: string;
  completed: boolean;
}

const initialItems: ChecklistItem[] = [
  { id: "profile", label: "Complete your profile", description: "Add your name, avatar, and bio", completed: true },
  { id: "team", label: "Invite your team", description: "Bring your collaborators on board", completed: true },
  { id: "project", label: "Create first project", description: "Set up your workspace and start building", completed: false },
  { id: "integrate", label: "Connect integrations", description: "Link your tools for a seamless workflow", completed: false },
  { id: "deploy", label: "Deploy to production", description: "Ship your first release to the world", completed: false },
];

export default function OnboardingChecklist() {
  const [items, setItems] = useState(initialItems);
  const completed = items.filter((i) => i.completed).length;
  const total = items.length;
  const progress = (completed / total) * 100;
  const allDone = completed === total;

  const toggle = (id: string) => {
    setItems((prev) => prev.map((i) => (i.id === id ? { ...i, completed: !i.completed } : i)));
  };

  return (
    <div className="py-10 px-6" style={{ background: "var(--color-background)" }}>
      <div className="mx-auto max-w-md">
        <div
          className="rounded-2xl p-6"
          style={{ background: "var(--color-background-card)", border: "1px solid var(--color-border)" }}
        >
          <div className="flex items-center justify-between">
            <h3 className="text-lg font-semibold" style={{ color: "var(--color-foreground)" }}>
              Getting Started
            </h3>
            {allDone && <PartyPopper size={20} style={{ color: "var(--color-accent)" }} />}
          </div>
          <p className="mt-1 text-sm" style={{ color: "var(--color-foreground-muted)" }}>
            Complete these steps to set up your workspace
          </p>

          {/* Progress bar */}
          <div className="mt-4 flex items-center gap-3">
            <div
              className="flex-1 h-2 rounded-full overflow-hidden"
              style={{ background: "var(--color-background-alt)" }}
            >
              <div
                className="h-full rounded-full transition-all duration-500 ease-out"
                style={{
                  background: allDone
                    ? "var(--color-accent)"
                    : `linear-gradient(90deg, var(--color-accent), color-mix(in srgb, var(--color-accent) 70%, var(--color-foreground)))`,
                  width: `${progress}%`,
                }}
              />
            </div>
            <span className="text-xs font-medium tabular-nums" style={{ color: "var(--color-foreground-muted)" }}>
              {completed}/{total}
            </span>
          </div>

          {/* Items */}
          <div className="mt-5 flex flex-col gap-1">
            {items.map((item) => (
              <button
                key={item.id}
                onClick={() => toggle(item.id)}
                className="flex items-start gap-3 w-full p-3 rounded-xl text-left transition-all duration-300"
                style={{
                  background: item.completed ? "var(--color-background-alt)" : "transparent",
                }}
              >
                <div className="mt-0.5 flex-shrink-0">
                  {item.completed ? (
                    <div
                      className="w-5 h-5 rounded-full flex items-center justify-center transition-transform duration-300"
                      style={{ background: "var(--color-accent)", transform: "scale(1)" }}
                    >
                      <Check size={12} style={{ color: "var(--color-background)" }} />
                    </div>
                  ) : (
                    <Circle size={20} style={{ color: "var(--color-border)" }} />
                  )}
                </div>
                <div>
                  <p
                    className="text-sm font-medium transition-all duration-300"
                    style={{
                      color: "var(--color-foreground)",
                      textDecoration: item.completed ? "line-through" : "none",
                      opacity: item.completed ? 0.6 : 1,
                    }}
                  >
                    {item.label}
                  </p>
                  <p className="text-xs mt-0.5" style={{ color: "var(--color-foreground-muted)" }}>
                    {item.description}
                  </p>
                </div>
              </button>
            ))}
          </div>

          {allDone && (
            <div
              className="mt-4 p-3 rounded-xl text-center text-sm font-medium"
              style={{
                background: "color-mix(in srgb, var(--color-accent) 10%, transparent)",
                color: "var(--color-accent)",
              }}
            >
              All done! Your workspace is ready.
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

Avis

Onboarding Checklist — React Onboarding Section — Incubator