
3D Extruded Buildings & Urban Footprints
Real-world 3D building extrusions with dynamic sunlight shadows, terrain DEM elevations, and pitch/bearing camera controls.
Progressive activation checklist card: header with title, an N of M complete counter and a linear Progress bar, tappable task rows with icon tiles, one-line descriptions and chevrons (completed tasks flip to a filled check tile with muted copy), a footer with a Skip for now ghost link and an X dismiss that fades the card out, and a success panel with a glowing check circle at 5 of 5. State is local — wire tasks to your own activation API.
Also available for Vue ->$pnpm dlx shadcn@latest add https://uipkge.dev/r/react/onboarding-checklist.json$npx shadcn@latest add https://uipkge.dev/r/react/onboarding-checklist.json$yarn dlx shadcn@latest add https://uipkge.dev/r/react/onboarding-checklist.json$bunx shadcn@latest add https://uipkge.dev/r/react/onboarding-checklist.jsonnpx shadcn@latest add @uipkge-react/onboarding-checklistInstalls to:components/blocks/| Name | Type / Values | Default | Required |
|---|---|---|---|
initialCompleteSeed every task as done so the success panel can be previewed in isolation. | boolean | — | optional |
className | string | — | optional |
Type aliases exported from this item's source. Use these to shape the data you pass in.
Taskinterface Task {
id: string
title: string
description: string
icon: LucideIcon
}'use client'
import * as React from 'react'
import { Bell, BookOpen, Building2, Check, ChevronRight, Database, Users, X } from 'lucide-react'
import type { LucideIcon } from 'lucide-react'
import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
import { Progress } from '@/components/ui/progress'
interface Task {
id: string
title: string
description: string
icon: LucideIcon
}
const baseTasks: Task[] = [
{
id: 'workspace',
title: 'Create workspace',
description: 'Name your team space and pick a URL.',
icon: Building2,
},
{
id: 'teammates',
title: 'Invite teammates',
description: 'Bring in the people you work with.',
icon: Users,
},
{
id: 'data',
title: 'Connect your data',
description: 'Sync sources so insights flow in automatically.',
icon: Database,
},
{
id: 'alerts',
title: 'Set up alerts',
description: 'Get notified when metrics move.',
icon: Bell,
},
{
id: 'api',
title: 'Explore API docs',
description: 'Learn the endpoints and ship your first request.',
icon: BookOpen,
},
]
export interface OnboardingChecklistProps {
/** Seed every task as done so the success panel can be previewed in isolation. */
initialComplete?: boolean
className?: string
}
export function OnboardingChecklist({ initialComplete = false, className }: OnboardingChecklistProps) {
const [tasks, setTasks] = React.useState(() =>
baseTasks.map((task, index) => ({ ...task, done: initialComplete || index < 3 })),
)
const [open, setOpen] = React.useState(true)
const completed = tasks.filter((task) => task.done).length
const total = tasks.length
const percent = Math.round((completed / total) * 100)
const allDone = completed === total
function toggle(id: string) {
setTasks((prev) => prev.map((task) => (task.id === id ? { ...task, done: !task.done } : task)))
}
return (
<div
data-slot="onboarding-checklist"
className={cn(
'mx-auto w-full max-w-md transition-[opacity,translate] duration-200 ease-out motion-reduce:transition-none',
!open && 'pointer-events-none translate-y-1 opacity-0',
className,
)}
>
<Card>
<CardHeader>
<CardTitle>Get started</CardTitle>
<CardDescription>Finish these steps to unlock everything your workspace can do.</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<div className="flex items-center justify-between">
<p className="text-muted-foreground text-xs">
<span className="text-foreground font-medium">
{completed} of {total}
</span>{' '}
complete
</p>
<p className="text-muted-foreground text-xs tabular-nums">{percent}%</p>
</div>
<Progress value={percent} />
</div>
{allDone ? (
<div className="space-y-4 py-6 text-center">
<div className="border-success/30 bg-success/10 text-success mx-auto flex size-16 items-center justify-center rounded-full border shadow-xs">
<Check className="size-8" aria-hidden="true" />
</div>
<div>
<p className="text-lg font-semibold">You're all set</p>
<p className="text-muted-foreground mt-1 text-sm">
Every step is complete — your workspace is ready to go.
</p>
</div>
</div>
) : (
<div className="-mx-2 divide-y">
{tasks.map((task) => {
const Icon = task.icon
return (
<button
key={task.id}
type="button"
role="checkbox"
aria-checked={task.done}
onClick={() => toggle(task.id)}
className="group hover:bg-muted/50 focus-visible:ring-ring flex w-full items-center gap-3 rounded-lg p-3 text-left transition-colors focus-visible:ring-2 focus-visible:outline-none"
>
{task.done ? (
<span className="bg-primary text-primary-foreground flex size-9 shrink-0 items-center justify-center rounded-md shadow-xs">
<Check className="size-4" aria-hidden="true" />
</span>
) : (
<span className="border-border bg-muted text-muted-foreground group-hover:bg-background group-hover:text-foreground flex size-9 shrink-0 items-center justify-center rounded-md border transition-colors">
<Icon className="size-4" aria-hidden="true" />
</span>
)}
<span className="min-w-0 flex-1">
<span className={cn('block truncate text-sm font-medium', task.done && 'text-muted-foreground')}>
{task.title}
</span>
<span className="text-muted-foreground mt-0.5 block truncate text-xs">{task.description}</span>
</span>
<ChevronRight
className="text-muted-foreground/50 size-4 shrink-0 transition-transform duration-200 group-hover:translate-x-0.5"
aria-hidden="true"
/>
</button>
)
})}
</div>
)}
</CardContent>
<CardFooter className="justify-between">
{!allDone ? (
<Button variant="ghost" size="sm" onClick={() => setOpen(false)}>
Skip for now
</Button>
) : (
<span aria-hidden="true" />
)}
<button
type="button"
aria-label="Dismiss checklist"
onClick={() => setOpen(false)}
className="hover:bg-foreground/10 focus-visible:ring-ring rounded-md p-1.5 transition-colors focus-visible:ring-2 focus-visible:outline-none"
>
<X className="text-muted-foreground size-4" aria-hidden="true" />
</button>
</CardFooter>
</Card>
</div>
)
}
Raw manifest:https://uipkge.dev/r/react/onboarding-checklist.json