Use Cases — Persona Tabs
blockmarketingPersona-switched use-case section: a tab list of roles swaps the pain-point summary, a three-item outcome list, a supporting metric, and a role-specific CTA without leaving the page.
Installation
$pnpm dlx uipkge-ng@latest add use-cases-persona-tabs$npx uipkge-ng@latest add use-cases-persona-tabs$yarn dlx uipkge-ng@latest add use-cases-persona-tabs$bunx uipkge-ng@latest add use-cases-persona-tabsManifest:
https://uipkge.dev/r/angular/use-cases-persona-tabs.jsonInstalls to:src/app/components/blocks/use-cases-persona-tabs/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.
Personainterface Persona {
value: string
role: string
pain: string
outcomes: string[]
metric: { value: string; label: string }
cta: string
}npm dependencies
Files installed (2)
src/app/components/blocks/use-cases-persona-tabs/use-cases-persona-tabs.component.ts6.4 kB
// Persona-switched use cases: selecting a role swaps the pain-point summary, outcome // list, supporting metric, and CTA in place — no navigation, no layout shift between // tabs. Port of the React block 1:1. import { ChangeDetectionStrategy, Component, Input, signal } from '@angular/core' import { ArrowRight, Check, LucideAngularModule } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiButtonComponent } from '@/ui/button/button.component' import { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component' import { UiTabsComponent, UiTabsContentComponent, UiTabsListComponent, UiTabsTriggerComponent, } from '@/ui/tabs/tabs.component' interface Persona { value: string role: string pain: string outcomes: string[] metric: { value: string; label: string } cta: string } const personas: Persona[] = [ { value: 'analytics', role: 'Data & analytics', pain: 'Every request becomes a bespoke SQL ticket, and nobody trusts the number in the deck.', outcomes: [ 'Publish one certified metric definition per number', 'Retire the ad-hoc query queue without losing coverage', 'Trace any figure back to the column it came from', ], metric: { value: '71%', label: 'fewer ad-hoc data requests after one quarter' }, cta: 'See the analyst workflow', }, { value: 'finance', role: 'Finance', pain: 'Close depends on four spreadsheets and one person who knows how they link.', outcomes: [ 'Reconcile against the ledger before anything publishes', 'Lock a period so restated figures cannot drift', 'Hand auditors a change log instead of a folder', ], metric: { value: '5 days', label: 'cut from the average monthly close' }, cta: 'See the close workflow', }, { value: 'revops', role: 'RevOps', pain: 'Pipeline reporting disagrees with the CRM, so forecast reviews start with an argument.', outcomes: [ 'One pipeline definition shared by sales and finance', 'Stage changes tracked with the rep and the timestamp', 'Forecast snapshots you can diff week over week', ], metric: { value: '2.4pp', label: 'tighter forecast accuracy band' }, cta: 'See the forecast workflow', }, { value: 'engineering', role: 'Engineering', pain: 'The reporting layer is an undocumented service nobody wants to own.', outcomes: [ 'Definitions live in the repo, reviewed like any other change', 'No bespoke ETL to babysit at 3am', 'Warehouse cost ceilings enforced per team', ], metric: { value: '0', label: 'bespoke pipelines to maintain' }, cta: 'See the engineering setup', }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-use-cases-persona-tabs, [ui-use-cases-persona-tabs]', 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, UiCardComponent, UiCardContentComponent, UiTabsComponent, UiTabsContentComponent, UiTabsListComponent, UiTabsTriggerComponent, ], template: ` <section data-slot="use-cases-persona-tabs" [class]="rootClass"> <div class="mx-auto max-w-5xl px-6 py-20 lg:py-28"> <div class="max-w-2xl"> <span ui-badge variant="secondary">Use cases</span> <h2 class="mt-4 text-3xl font-semibold tracking-tight sm:text-4xl">Pick the seat you sit in</h2> <p class="text-muted-foreground mt-3 text-lg"> Same platform, four very different first weeks. Start with the one that matches your job. </p> </div> <ui-tabs [value]="activeTab()" (valueChange)="activeTab.set($event)" class="mt-10"> <!-- flex-nowrap + horizontal scroll: the trigger row keeps its height whether there are three roles or thirty. --> <ui-tabs-list variant="segmented" class="flex-nowrap overflow-x-auto"> @for (persona of personas; track persona.value) { <button ui-tabs-trigger [value]="persona.value" variant="segmented"> {{ persona.role }} </button> } </ui-tabs-list> @for (persona of personas; track persona.value) { <ui-tabs-content [value]="persona.value" class="mt-8"> <!-- min-h keeps the panel from collapsing between tabs with shorter copy. --> <div class="grid min-h-[19rem] gap-8 lg:grid-cols-[1.3fr_1fr]"> <div> <p class="text-muted-foreground text-sm">Today</p> <p class="mt-1.5 text-lg leading-snug font-medium text-balance">{{ persona.pain }}</p> <p class="text-muted-foreground mt-7 text-sm">With uipkge</p> <ul class="mt-3 space-y-3"> @for (outcome of persona.outcomes; track outcome) { <li class="flex items-start gap-2.5 text-sm"> <lucide-icon [img]="Check" class="text-primary mt-0.5 size-4 shrink-0" aria-hidden="true" /> <span>{{ outcome }}</span> </li> } </ul> <button ui-button class="mt-7"> {{ persona.cta }} <lucide-icon [img]="ArrowRight" class="ml-2 size-4" aria-hidden="true" /> </button> </div> <div ui-card class="self-start"> <div ui-card-content class="p-6"> <p class="font-display text-4xl font-bold tracking-tight">{{ persona.metric.value }}</p> <p class="text-muted-foreground mt-2 text-sm leading-relaxed">{{ persona.metric.label }}</p> </div> </div> </div> </ui-tabs-content> } </ui-tabs> </div> </section> `, }) export class UiUseCasesPersonaTabsComponent { protected readonly ArrowRight = ArrowRight protected readonly Check = Check protected readonly personas = personas @Input('class') className?: string @Input() set initialPersona(v: string | undefined) { if (v) this.activeTab.set(v) } readonly activeTab = signal('analytics') get rootClass(): string { return cn('bg-background', this.className) } }src/app/components/blocks/use-cases-persona-tabs/index.ts0.1 kB
export { UiUseCasesPersonaTabsComponent } from './use-cases-persona-tabs.component'
Raw manifest:https://uipkge.dev/r/angular/use-cases-persona-tabs.json