# Welcome Checklist Card

> Onboarding welcome card with a setup progress bar and three checkbox steps for reading docs, inviting the team, and configuring the workspace.

A shadcn/ui block from the [@7ovr](https://7ovr.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/7ovr/onboarding-1).

- Page, with a live preview: https://shadcn.exchange/r/7ovr/onboarding-1
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/7ovr/onboarding-1-light-e186660b76b33ffa.png
- Registry item: https://7ovr.com/r/onboarding-1.json
- Categories: onboarding
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://7ovr.com/r/onboarding-1.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `@base-ui/react`
- Registry items: `button`, `card`, `checkbox`, `progress`, `separator`

## Files

### `registry/blocks/onboarding/1/onboarding-block.tsx`

```tsx
"use client"

import * as React from "react"
import { Button } from "@/components/ui/button"
import {
  Card,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import { Checkbox } from "@/components/ui/checkbox"
import { Progress } from "@/components/ui/progress"
import { Separator } from "@/components/ui/separator"
import { cn } from "@/lib/utils"
import { IconPlaceholder } from "@/components/icons/icon-placeholder"

type IconProps = { className?: string; size?: number | string }

const STEPS = [
  {
    id: "docs",
    icon: (p: IconProps) => (
      <IconPlaceholder
        lucide="BookOpen"
        tabler="IconBook2"
        hugeicons="BookOpenIcon"
        phosphor="BookOpen"
        remixicon="RiBookOpenLine"
        {...p}
      />
    ),
    title: "Read the docs",
    description:
      "Explore guides and API references to understand the platform.",
  },
  {
    id: "team",
    icon: (p: IconProps) => (
      <IconPlaceholder
        lucide="Users"
        tabler="IconUsersGroup"
        hugeicons="UserGroupIcon"
        phosphor="UsersThree"
        remixicon="RiTeamLine"
        {...p}
      />
    ),
    title: "Invite your team",
    description: "Bring your colleagues on board and assign roles.",
  },
  {
    id: "workspace",
    icon: (p: IconProps) => (
      <IconPlaceholder
        lucide="Settings"
        tabler="IconSettings"
        hugeicons="SettingsIcon"
        phosphor="GearSix"
        remixicon="RiSettings4Line"
        {...p}
      />
    ),
    title: "Configure your workspace",
    description: "Set up integrations, notifications, and preferences.",
  },
]

export default function OnboardingBlock() {
  const [done, setDone] = React.useState<Record<string, boolean>>({
    docs: true,
  })

  const completed = STEPS.filter((s) => done[s.id]).length
  const total = STEPS.length
  const pct = Math.round((completed / total) * 100)
  const allDone = completed === total

  function toggle(id: string, checked: boolean) {
    setDone((prev) => ({ ...prev, [id]: checked }))
  }

  return (
    <section className="flex w-full items-center justify-center bg-background px-6 py-12 text-foreground">
      <Card className="w-full max-w-md">
        <CardHeader>
          <CardTitle className="text-base font-semibold">
            Welcome To Acme
          </CardTitle>
          <CardDescription>
            Let&apos;s get your workspace ready in just a few steps. Complete
            the checklist below to unlock everything Acme has to offer.
          </CardDescription>
          <div className="mt-3 flex flex-col gap-2">
            <div className="flex items-center justify-between text-xs">
              <span className="font-medium text-foreground">
                Setup progress
              </span>
              <span className="text-muted-foreground tabular-nums">
                <span className="font-medium text-foreground">{completed}</span>{" "}
                of {total}
              </span>
            </div>
            <Progress
              value={pct}
              aria-label={`${completed} of ${total} steps completed`}
            />
          </div>
        </CardHeader>

        <CardContent className="flex flex-col gap-0">
          <Separator />
          {STEPS.map((step, index) => {
            const Icon = step.icon
            const isDone = !!done[step.id]
            return (
              <div key={step.id}>
                <label className="flex cursor-pointer items-start gap-3 py-4">
                  <div
                    className={cn(
                      "flex size-8 shrink-0 items-center justify-center rounded-lg",
                      isDone
                        ? "bg-primary text-primary-foreground"
                        : "bg-muted text-muted-foreground"
                    )}
                  >
                    <Icon className="size-4" aria-hidden="true" />
                  </div>
                  <div className="flex flex-1 flex-col gap-0.5">
                    <span
                      className={cn(
                        "text-xs font-medium",
                        isDone
                          ? "text-muted-foreground line-through"
                          : "text-foreground"
                      )}
                    >
                      {step.title}
                    </span>
                    <span className="text-xs text-muted-foreground">
                      {step.description}
                    </span>
                  </div>
                  <Checkbox
                    checked={isDone}
                    onCheckedChange={(checked) =>
                      toggle(step.id, checked === true)
                    }
                    aria-label={`Mark "${step.title}" complete`}
                    className="mt-0.5"
                  />
                </label>
                {index < STEPS.length - 1 && <Separator />}
              </div>
            )
          })}
          <Separator />
        </CardContent>

        <CardFooter>
          <Button className="w-full" disabled={!allDone}>
            {allDone ? "Finish Setup" : "Get Started"}
            <IconPlaceholder
              lucide="ArrowRight"
              tabler="IconArrowRight"
              hugeicons="ArrowRightIcon"
              phosphor="ArrowRight"
              remixicon="RiArrowRightLine"
              data-icon="inline-end"
              aria-hidden="true"
            />
          </Button>
        </CardFooter>
      </Card>
    </section>
  )
}
```
