How It Works — Three Steps
blockmarketingHorizontal three-step explainer with numbered markers on a connector rail, per-step icon, body copy, and a time estimate, closing on a primary + ghost CTA row.
Installation
$pnpm dlx uipkge-ng@latest add how-it-works-steps$npx uipkge-ng@latest add how-it-works-steps$yarn dlx uipkge-ng@latest add how-it-works-steps$bunx uipkge-ng@latest add how-it-works-stepsManifest:
https://uipkge.dev/r/angular/how-it-works-steps.jsonInstalls to:src/app/components/blocks/how-it-works-steps/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.
HowItWorksStepinterface HowItWorksStep {
icon: LucideIconData
title: string
body: string
detail: string
}npm dependencies
Files installed (2)
src/app/components/blocks/how-it-works-steps/how-it-works-steps.component.ts4.2 kB
// Horizontal three-step explainer with numbered markers on a connector rail, per-step // icon, body copy, and a time estimate, closing on a primary + ghost CTA row. // Port of the React block 1:1. import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { CloudUpload, LucideAngularModule, Plug, Rocket, type LucideIconData } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiButtonComponent } from '@/ui/button/button.component' export interface HowItWorksStep { icon: LucideIconData title: string body: string detail: string } const steps: HowItWorksStep[] = [ { icon: Plug, title: 'Connect your stack', body: 'Point us at Postgres, Snowflake, or a CSV drop. Read-only credentials, no agent to install.', detail: '~4 min', }, { icon: CloudUpload, title: 'Map your metrics', body: 'Pick the columns that matter. We infer types, generate the schema, and flag anything ambiguous.', detail: '~10 min', }, { icon: Rocket, title: 'Ship the dashboard', body: 'Publish to your team with row-level permissions already applied. Iterate without a redeploy.', detail: 'Same day', }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-how-it-works-steps, [ui-how-it-works-steps]', standalone: true, // React renders the root <section> itself: the host stays out of layout and `class` goes to the root. host: { '[attr.class]': '"contents"' }, imports: [LucideAngularModule, UiBadgeComponent, UiButtonComponent], template: ` <section data-slot="how-it-works-steps" [class]="rootClass"> <div class="mx-auto max-w-6xl px-6 py-20 lg:py-28"> <div class="max-w-2xl"> <span ui-badge variant="secondary">How it works</span> <h2 class="mt-4 text-3xl font-semibold tracking-tight sm:text-4xl"> Three steps from credentials to dashboard </h2> <p class="text-muted-foreground mt-3 text-lg"> No migration, no rewrite. Most teams finish the whole path in an afternoon. </p> </div> <div class="relative mt-14"> <!-- Connector rail: decorative, so it lives outside the <ol> rather than adding a phantom list item for screen readers. --> <div class="bg-border pointer-events-none absolute top-6 right-[16.67%] left-[16.67%] hidden h-px md:block" aria-hidden="true" ></div> <ol class="grid gap-10 md:grid-cols-3 md:gap-8"> @for (step of allSteps; track step.title; let index = $index) { <li class="relative flex flex-col items-start md:items-center"> <div class="border-border bg-card text-foreground relative z-10 flex size-12 items-center justify-center rounded-full border font-mono text-sm font-semibold" > {{ pad(index + 1) }} </div> <div class="mt-5 md:text-center"> <lucide-icon [img]="step.icon" class="text-muted-foreground mb-3 size-5 md:mx-auto" aria-hidden="true" /> <h3 class="text-base font-semibold">{{ step.title }}</h3> <p class="text-muted-foreground mt-2 max-w-xs text-sm leading-relaxed md:mx-auto">{{ step.body }}</p> <p class="text-muted-foreground/80 mt-3 font-mono text-xs tracking-wide uppercase"> {{ step.detail }} </p> </div> </li> } </ol> </div> <div class="border-border mt-14 flex flex-wrap items-center gap-4 border-t pt-8"> <button ui-button>Start the walkthrough</button> <button ui-button variant="ghost">Read the setup guide</button> </div> </div> </section> `, }) export class UiHowItWorksStepsComponent { protected readonly allSteps = steps @Input('class') className?: string get rootClass(): string { return cn('bg-background', this.className) } pad(n: number): string { return String(n).padStart(2, '0') } }src/app/components/blocks/how-it-works-steps/index.ts0.1 kB
export { UiHowItWorksStepsComponent, type HowItWorksStep } from './how-it-works-steps.component'
Raw manifest:https://uipkge.dev/r/angular/how-it-works-steps.json