Onboarding Checklist
blockauthProgressive 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.
Installation
$pnpm dlx uipkge-ng@latest add onboarding-checklist$npx uipkge-ng@latest add onboarding-checklist$yarn dlx uipkge-ng@latest add onboarding-checklist$bunx uipkge-ng@latest add onboarding-checklistManifest:
https://uipkge.dev/r/angular/onboarding-checklist.jsonInstalls to:src/app/components/blocks/onboarding-checklist/Variants
Loading interactive previews…
Loading interactive previews…
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
OnboardingChecklistTaskinterface OnboardingChecklistTask {
id: string
title: string
description: string
icon: LucideIconData
done: boolean
}npm dependencies
Files installed (2)
src/app/components/blocks/onboarding-checklist/onboarding-checklist.component.ts8.2 kB
// Edit this file to change the tasks and copy. State is local -- wire `toggle()` and // `dismiss()` to your own activation API. import { Component, Input, OnInit, booleanAttribute, computed, signal, ChangeDetectionStrategy } from '@angular/core' import { Bell, BookOpen, Building2, Check, ChevronRight, Database, LucideAngularModule, Users, X, type LucideIconData, } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiButtonComponent } from '@/ui/button/button.component' import { UiCardComponent, UiCardContentComponent, UiCardDescriptionComponent, UiCardFooterComponent, UiCardHeaderComponent, UiCardTitleComponent, } from '@/ui/card/card.component' import { UiProgressComponent } from '@/ui/progress/progress.component' export interface OnboardingChecklistTask { id: string title: string description: string icon: LucideIconData done: boolean } const baseTasks: Omit<OnboardingChecklistTask, '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, }, ] /** * OnboardingChecklist -- the React block, 1:1: a card with an "N of M complete" counter and * a progress bar over tappable task rows (a done task flips to a filled check tile with muted * copy). "Skip for now" and the X fade the card out; at 5 of 5 the rows become a success panel. */ @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-onboarding-checklist, [ui-onboarding-checklist]', standalone: true, // React renders the root <div> itself: the host stays out of layout and `class` goes to the root. host: { '[attr.class]': '"contents"' }, imports: [ LucideAngularModule, UiButtonComponent, UiCardComponent, UiCardHeaderComponent, UiCardTitleComponent, UiCardDescriptionComponent, UiCardContentComponent, UiCardFooterComponent, UiProgressComponent, ], template: ` <div data-slot="onboarding-checklist" [class]="rootClass"> <ui-card> <ui-card-header> <ui-card-title>Get started</ui-card-title> <ui-card-description>Finish these steps to unlock everything your workspace can do.</ui-card-description> </ui-card-header> <ui-card-content 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> <ui-progress [value]="percent()" /> </div> @if (allDone()) { <div 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" > <lucide-icon [img]="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> } @else { <div class="-mx-2 divide-y"> @for (task of tasks(); track task.id) { <button type="button" role="checkbox" [attr.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)" > @if (task.done) { <span class="bg-primary text-primary-foreground flex size-9 shrink-0 items-center justify-center rounded-md shadow-xs" > <lucide-icon [img]="Check" class="size-4" aria-hidden="true" /> </span> } @else { <span 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" > <lucide-icon [img]="task.icon" class="size-4" aria-hidden="true" /> </span> } <span class="min-w-0 flex-1"> <span [class]="titleClass(task.done)">{{ task.title }}</span> <span class="text-muted-foreground mt-0.5 block truncate text-xs">{{ task.description }}</span> </span> <!-- [class] (not class=) feeds only the svg, so the hover nudge is not applied twice (host + svg). --> <lucide-icon [img]="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> } </ui-card-content> <ui-card-footer class="justify-between"> @if (!allDone()) { <button ui-button variant="ghost" size="sm" (click)="dismiss()">Skip for now</button> } @else { <span 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()" > <lucide-icon [img]="X" class="text-muted-foreground size-4" aria-hidden="true" /> </button> </ui-card-footer> </ui-card> </div> `, }) export class UiOnboardingChecklistComponent implements OnInit { /** Seed every task as done so the success panel can be previewed in isolation. */ @Input({ transform: booleanAttribute }) initialComplete = false @Input('class') className?: string readonly tasks = signal<OnboardingChecklistTask[]>(this.seed()) readonly open = signal(true) readonly completed = computed(() => this.tasks().filter((task) => task.done).length) readonly total = computed(() => this.tasks().length) readonly percent = computed(() => Math.round((this.completed() / this.total()) * 100)) readonly allDone = computed(() => this.completed() === this.total()) protected get rootClass(): string { return cn( 'mx-auto w-full max-w-md transition-[opacity,translate] duration-200 ease-out motion-reduce:transition-none', !this.open() && 'pointer-events-none translate-y-1 opacity-0', this.className, ) } protected readonly Check = Check protected readonly ChevronRight = ChevronRight protected readonly X = X ngOnInit(): void { // React seeds its useState once from `initialComplete`; inputs land after construction. this.tasks.set(this.seed()) } toggle(id: string): void { this.tasks.update((prev) => prev.map((task) => (task.id === id ? { ...task, done: !task.done } : task))) } dismiss(): void { this.open.set(false) } protected titleClass(done: boolean): string { return cn('block truncate text-sm font-medium', done && 'text-muted-foreground') } private seed(): OnboardingChecklistTask[] { return baseTasks.map((task, index) => ({ ...task, done: this.initialComplete || index < 3 })) } }src/app/components/blocks/onboarding-checklist/index.ts0.1 kB
export { UiOnboardingChecklistComponent, type OnboardingChecklistTask } from './onboarding-checklist.component'
Raw manifest:https://uipkge.dev/r/angular/onboarding-checklist.json