Customer Metrics Band
blockmarketingCustomer velocity and ROI telemetry band with cohort segmentation (Enterprise vs High-Growth), 4 verified KPI metric tiles, and deep-dive impact inspector.
Installation
$pnpm dlx uipkge-ng@latest add customer-metrics-band$npx uipkge-ng@latest add customer-metrics-band$yarn dlx uipkge-ng@latest add customer-metrics-band$bunx uipkge-ng@latest add customer-metrics-bandManifest:
https://uipkge.dev/r/angular/customer-metrics-band.jsonInstalls to:src/app/components/blocks/customer-metrics-band/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.
MetricStatinterface MetricStat {
id: string
label: string
value: string
baseline: string
change: string
isPositive: boolean
description: string
details: {
before: string
after: string
impact: string
}
}npm dependencies
Files installed (2)
src/app/components/blocks/customer-metrics-band/customer-metrics-band.component.ts13.3 kB
// Customer velocity and ROI telemetry band with cohort segmentation (Enterprise vs // High-Growth), 4 verified KPI metric tiles, and a deep-dive impact inspector. // Port of the React block 1:1. import { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core' import { Gauge, LucideAngularModule } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiCardComponent } from '@/ui/card/card.component' export type CustomerCohort = 'all' | 'enterprise' | 'startups' export interface MetricStat { id: string label: string value: string baseline: string change: string isPositive: boolean description: string details: { before: string after: string impact: string } } const metricsByCohort: Record<CustomerCohort, MetricStat[]> = { all: [ { id: 'velocity', label: 'Time to First Production Release', value: '4.2x', baseline: 'Reduced from 14 weeks to 18 days', change: '+320% Faster', isPositive: true, description: 'Engineering squads ship complete vertical workflows without custom CSS boilerplate.', details: { before: '98 Days average', after: '18 Days average', impact: '80 days saved per major launch' }, }, { id: 'vitals', label: 'Core Web Vitals Pass Rate', value: '99.4%', baseline: 'Tested across 10M+ mobile pageviews', change: '+28.4% vs monolithic', isPositive: true, description: 'OKLCH zero-runtime styling ensures sub-50ms INP and pristine Lighthouse 100 scores.', details: { before: '71.0% Pass Rate', after: '99.4% Pass Rate', impact: 'Zero layout shift (CLS 0.00)' }, }, { id: 'maintenance', label: 'Design System Maintenance Cost', value: '-74%', baseline: 'Engineering hours spent upgrading packages', change: '-74% Reduction', isPositive: true, description: 'Direct code ownership eliminates breaking semver dependency conflicts permanently.', details: { before: '24 hrs / developer / mo', after: '6.2 hrs / developer / mo', impact: '17.8 hrs reclaimed' }, }, { id: 'cloud_savings', label: 'Average Annual Compute & Bandwidth Saved', value: '$184k', baseline: 'Calculated across high-traffic SaaS suites', change: 'Zero AST bloat', isPositive: true, description: 'Zero unused component CSS in bundle output lowers edge bandwidth distribution costs.', details: { before: '640 kB vendor chunks', after: '38 kB tree-shaken chunks', impact: '94% payload reduction' }, }, ], enterprise: [ { id: 'velocity', label: 'Time to First Production Release', value: '5.8x', baseline: 'Enterprise multi-app design alignment', change: '+480% Faster', isPositive: true, description: 'Standardized AST across 20+ squads without package coordination overhead.', details: { before: '180 Days average', after: '31 Days average', impact: '149 days saved per project' }, }, { id: 'vitals', label: 'Core Web Vitals Pass Rate', value: '99.8%', baseline: 'Internal enterprise portals & dashboards', change: '+34.2% uplift', isPositive: true, description: 'Strict keyboard ergonomics and WCAG AA accessibility out of the box.', details: { before: '65.6% Pass Rate', after: '99.8% Pass Rate', impact: '100% compliance audit pass' }, }, { id: 'maintenance', label: 'Design System Maintenance Cost', value: '-82%', baseline: 'Eliminated dedicated registry sync team', change: '-82% Saved', isPositive: true, description: 'Engineers customize source code in their own repo without waiting on upstream merges.', details: { before: '3 FTEs maintenance', after: '0.5 FTE maintenance', impact: '2.5 FTEs reallocated to product', }, }, { id: 'cloud_savings', label: 'Average Annual Compute & Bandwidth Saved', value: '$420k', baseline: 'Tier-1 enterprise infrastructure', change: 'Zero CVE risk', isPositive: true, description: 'No third-party runtime dependencies eliminates transitive package auditing costs.', details: { before: '$650k vendor licensing', after: '$230k infrastructure', impact: '$420k saved annually' }, }, ], startups: [ { id: 'velocity', label: 'Time to First Production Release', value: '6.4x', baseline: 'From idea to paying users in days', change: '+540% Faster', isPositive: true, description: 'Plug-and-play workbenches and CRM blocks ready for Stripe integration on day 1.', details: { before: '45 Days to MVP', after: '7 Days to MVP', impact: '38 days faster to revenue' }, }, { id: 'vitals', label: 'Core Web Vitals Pass Rate', value: '99.1%', baseline: 'Landing pages and conversion flows', change: '+22.0% uplift', isPositive: true, description: 'Blazing fast mobile experiences drive higher conversion rates for early users.', details: { before: '77.1% Pass Rate', after: '99.1% Pass Rate', impact: '+14% checkout conversion' }, }, { id: 'maintenance', label: 'Design System Maintenance Cost', value: '-90%', baseline: 'Zero full-time design engineers needed', change: '-90% Overhead', isPositive: true, description: 'Founders and full-stack devs build world-class Linear-grade interfaces independently.', details: { before: 'Hiring UI designer ($140k)', after: 'Self-serve registry ($0)', impact: '$140k runway preserved', }, }, { id: 'cloud_savings', label: 'Average Annual Compute & Bandwidth Saved', value: '$48k', baseline: 'Vercel / Cloudflare edge usage tier', change: 'Sub-millisecond cold start', isPositive: true, description: 'Compact bundle sizes prevent edge function invocation timeouts and memory spikes.', details: { before: '350ms cold starts', after: '42ms cold starts', impact: '88% compute reduction' }, }, ], } const cohortOptions: { id: CustomerCohort; label: string }[] = [ { id: 'all', label: 'All Teams' }, { id: 'enterprise', label: 'Enterprise 500' }, { id: 'startups', label: 'High-Growth' }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-customer-metrics-band, [ui-customer-metrics-band]', 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, UiCardComponent], template: ` <section data-slot="customer-metrics-band" [class]="rootClass"> <div class="mx-auto max-w-6xl space-y-12"> <!-- Section Header with Cohort Switcher --> <div class="border-border/60 flex flex-col items-center justify-between gap-6 border-b pb-8 text-center md:flex-row md:text-left" > <div class="space-y-2"> <span ui-badge variant="secondary" class="gap-1.5 px-3 py-1 font-mono text-xs shadow-xs"> <lucide-icon [img]="Gauge" class="text-primary size-3.5" /> Verified Customer Impact Benchmark </span> <h2 class="text-foreground text-3xl font-bold tracking-tight"> Proven engineering velocity at global scale. </h2> <p class="text-muted-foreground text-xs sm:text-sm"> Aggregated telemetry from over 1,400+ production applications built with UIPKGE. </p> </div> <!-- Cohort Switcher Tabs --> <div class="bg-background border-border flex shrink-0 items-center gap-1 rounded-xl border p-1"> @for (c of cohortOptions; track c.id) { <button type="button" [class]="cohortButtonClass(c.id)" (click)="activeCohort.set(c.id)"> {{ c.label }} </button> } </div> </div> <!-- 4-Tile KPI Metric Cards Grid --> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4"> @for (stat of currentMetricList(); track stat.id) { <div ui-card role="button" tabindex="0" [attr.aria-pressed]="activeMetricId() === stat.id" [class]="metricCardClass(stat.id)" (click)="activeMetricId.set(stat.id)" (keydown.enter)="activeMetricId.set(stat.id)" (keydown.space)="selectMetricKey(stat.id, $event)" > <div class="space-y-2"> <div class="flex items-center justify-between"> <span class="text-muted-foreground max-w-[160px] truncate font-mono text-xs">{{ stat.label }}</span> <span ui-badge variant="outline" class="border-success/20 bg-success/10 text-success font-mono text-xs" > {{ stat.change }} </span> </div> <p class="text-foreground font-mono text-3xl font-bold tracking-tight sm:text-4xl">{{ stat.value }}</p> </div> <p class="text-muted-foreground border-border/60 border-t pt-2 text-xs leading-relaxed"> {{ stat.baseline }} </p> </div> } </div> <!-- Interactive Detailed Breakdown Card for Selected KPI --> <div ui-card class="border-border bg-card/95 space-y-6 overflow-hidden rounded-2xl p-6 text-left shadow-xl backdrop-blur-md sm:p-8" > <div class="border-border flex flex-col items-start justify-between gap-4 border-b pb-4 sm:flex-row sm:items-center" > <div class="space-y-1"> <div class="flex items-center gap-2"> <span class="bg-primary size-2 rounded-full"></span> <h3 class="text-foreground font-mono text-base font-bold"> {{ selectedMetric().label }} — Deep Dive </h3> </div> <p class="text-muted-foreground text-xs">{{ selectedMetric().description }}</p> </div> <span ui-badge variant="secondary" class="text-primary font-mono text-xs font-semibold"> Cohort: {{ activeCohort().toUpperCase() }} </span> </div> <div class="grid grid-cols-1 gap-4 sm:grid-cols-3"> <div class="border-border bg-background/50 space-y-1 rounded-xl border p-4"> <p class="text-muted-foreground font-mono text-xs">Monolithic Package Baseline</p> <p class="text-muted-foreground font-mono text-lg font-bold line-through"> {{ selectedMetric().details.before }} </p> </div> <div class="border-border bg-background/50 space-y-1 rounded-xl border p-4"> <p class="text-success font-mono text-xs font-semibold">UIPKGE Unbundled Registry</p> <p class="text-success font-mono text-lg font-bold"> {{ selectedMetric().details.after }} </p> </div> <div class="border-border bg-primary/5 space-y-1 rounded-xl border p-4"> <p class="text-primary font-mono text-xs font-semibold">Net Engineering Impact</p> <p class="text-foreground font-mono text-lg font-bold">{{ selectedMetric().details.impact }}</p> </div> </div> </div> </div> </section> `, }) export class UiCustomerMetricsBandComponent { protected readonly Gauge = Gauge protected readonly cohortOptions = cohortOptions @Input() set initialCohort(v: CustomerCohort) { if (v) this.activeCohort.set(v) } @Input() set initialMetricId(v: string) { if (v) this.activeMetricId.set(v) } @Input('class') className?: string readonly activeCohort = signal<CustomerCohort>('all') readonly activeMetricId = signal<string>('velocity') readonly currentMetricList = computed(() => metricsByCohort[this.activeCohort()]) readonly selectedMetric = computed(() => { const list = this.currentMetricList() return list.find((m) => m.id === this.activeMetricId()) ?? list[0] }) get rootClass(): string { return cn( 'border-border/80 bg-muted/10 relative overflow-hidden border-y px-4 py-16 sm:px-6 sm:py-24 lg:px-8', this.className, ) } cohortButtonClass(id: CustomerCohort): string { // [class] replaces static classes: the base must live here (matches React's cn(base, active)). return cn( 'focus-visible:ring-ring rounded-lg px-3 py-1.5 font-mono text-xs transition-colors focus-visible:ring-2 focus-visible:outline-none', this.activeCohort() === id ? 'bg-primary text-primary-foreground font-semibold shadow-xs' : 'text-muted-foreground hover:text-foreground', ) } metricCardClass(id: string): string { return cn( 'border-border bg-card/90 focus-visible:ring-ring flex cursor-pointer flex-col justify-between space-y-4 rounded-2xl p-5 shadow-sm transition-colors hover:shadow-md focus-visible:ring-2 focus-visible:outline-none', this.activeMetricId() === id && 'ring-primary border-primary/60 bg-card ring-2', ) } selectMetricKey(id: string, event: KeyboardEvent): void { event.preventDefault() this.activeMetricId.set(id) } }src/app/components/blocks/customer-metrics-band/index.ts0.1 kB
export { UiCustomerMetricsBandComponent, type CustomerCohort, type MetricStat } from './customer-metrics-band.component'
Raw manifest:https://uipkge.dev/r/angular/customer-metrics-band.json