
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 React ->$pnpm dlx shadcn-vue@latest add https://uipkge.dev/r/vue/onboarding-checklist.json$npx shadcn-vue@latest add https://uipkge.dev/r/vue/onboarding-checklist.json$yarn dlx shadcn-vue@latest add https://uipkge.dev/r/vue/onboarding-checklist.json$bunx shadcn-vue@latest add https://uipkge.dev/r/vue/onboarding-checklist.jsonnpx shadcn-vue@latest add @uipkge/onboarding-checklistInstalls to:app/components/blocks/| Name | Type / Values | Default | Required |
|---|---|---|---|
initialCompleteSeed every task as done so the success panel can be previewed in isolation. | boolean | false | optional |
class | HTMLAttributes['class'] | — | 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: Component
done: boolean
}<script setup lang="ts">
import { computed, ref } from 'vue'
import type { Component } from 'vue'
import type { HTMLAttributes } from 'vue'
import { Bell, BookOpen, Building2, Check, ChevronRight, Database, Users, X } from 'lucide-vue-next'
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: Component
done: boolean
}
const props = withDefaults(
defineProps<{
/** Seed every task as done so the success panel can be previewed in isolation. */
initialComplete?: boolean
class?: HTMLAttributes['class']
}>(),
{
initialComplete: false,
},
)
const baseTasks: Omit<Task, 'done'>[] = [
{
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,
},
]
const tasks = ref<Task[]>(
baseTasks.map((task, index) => ({
...task,
done: props.initialComplete || index < 3,
})),
)
const open = ref(true)
const completed = computed(() => tasks.value.filter((task) => task.done).length)
const total = computed(() => tasks.value.length)
const percent = computed(() => Math.round((completed.value / total.value) * 100))
const allDone = computed(() => completed.value === total.value)
function toggle(id: string) {
const task = tasks.value.find((t) => t.id === id)
if (task) task.done = !task.done
}
function dismiss() {
open.value = false
}
</script>
<template>
<div
data-slot="onboarding-checklist"
:class="
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',
props.class,
)
"
>
<Card>
<CardHeader>
<CardTitle>Get started</CardTitle>
<CardDescription>Finish these steps to unlock everything your workspace can do.</CardDescription>
</CardHeader>
<CardContent class="space-y-4">
<div class="space-y-2">
<div class="flex items-center justify-between">
<p class="text-muted-foreground text-xs">
<span class="text-foreground font-medium">{{ completed }} of {{ total }}</span>
complete
</p>
<p class="text-muted-foreground text-xs tabular-nums">{{ percent }}%</p>
</div>
<Progress :model-value="percent" />
</div>
<div v-if="allDone" class="space-y-4 py-6 text-center">
<div
class="border-success/30 bg-success/10 text-success mx-auto flex size-16 items-center justify-center rounded-full border shadow-xs"
>
<Check class="size-8" aria-hidden="true" />
</div>
<div>
<p class="text-lg font-semibold">You're all set</p>
<p class="text-muted-foreground mt-1 text-sm">Every step is complete — your workspace is ready to go.</p>
</div>
</div>
<div v-else class="-mx-2 divide-y">
<button
v-for="task in tasks"
:key="task.id"
type="button"
role="checkbox"
:aria-checked="task.done"
class="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"
@click="toggle(task.id)"
>
<span
v-if="task.done"
class="bg-primary text-primary-foreground flex size-9 shrink-0 items-center justify-center rounded-md shadow-xs"
>
<Check class="size-4" aria-hidden="true" />
</span>
<span
v-else
class="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"
>
<component :is="task.icon" class="size-4" aria-hidden="true" />
</span>
<span class="min-w-0 flex-1">
<span :class="cn('block truncate text-sm font-medium', task.done && 'text-muted-foreground')">
{{ task.title }}
</span>
<span class="text-muted-foreground mt-0.5 block truncate text-xs">{{ task.description }}</span>
</span>
<ChevronRight
class="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 class="justify-between">
<Button v-if="!allDone" variant="ghost" size="sm" @click="dismiss">Skip for now</Button>
<span v-else aria-hidden="true"></span>
<button
type="button"
aria-label="Dismiss checklist"
class="hover:bg-foreground/10 focus-visible:ring-ring rounded-md p-1.5 transition-colors focus-visible:ring-2 focus-visible:outline-none"
@click="dismiss"
>
<X class="text-muted-foreground size-4" aria-hidden="true" />
</button>
</CardFooter>
</Card>
</div>
</template>
Raw manifest:https://uipkge.dev/r/vue/onboarding-checklist.json