CTA — Split Panel
blockmarketingTwo-path closing section splitting self-serve from sales-assisted, each side stating who it suits and what happens next so the choice is not a guess.
Installation
$pnpm dlx uipkge-ng@latest add cta-split-panel$npx uipkge-ng@latest add cta-split-panel$yarn dlx uipkge-ng@latest add cta-split-panel$bunx uipkge-ng@latest add cta-split-panelManifest:
https://uipkge.dev/r/angular/cta-split-panel.jsonInstalls to:src/app/components/blocks/cta-split-panel/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.
CtaPathinterface CtaPath {
icon: LucideIconData
label: string
suits: string
next: string[]
cta: string
variant: 'default' | 'outline'
note: string
}npm dependencies
Files installed (2)
src/app/components/blocks/cta-split-panel/cta-split-panel.component.ts4.5 kB
// Two-path closing section splitting self-serve from sales-assisted, each side stating // who it suits and what happens next so the choice is not a guess. // Port of the React block 1:1. import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { ArrowRight, Headphones, LucideAngularModule, Zap, type LucideIconData } 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 { UiSeparatorComponent } from '@/ui/separator/separator.component' interface CtaPath { icon: LucideIconData label: string suits: string next: string[] cta: string variant: 'default' | 'outline' note: string } // Each path says who it suits and what happens next. A two-button choice with // no stated difference just makes the reader pick the bigger button. const paths: CtaPath[] = [ { icon: Zap, label: 'Start it yourself', suits: 'One team, an existing warehouse, and someone comfortable opening a pull request.', next: ['Connect a read-only role', 'Publish one certified metric', 'Invite the rest of the team'], cta: 'Start free', variant: 'default', note: 'No card. Cancel from the dashboard.', }, { icon: Headphones, label: 'Have us run it', suits: 'Multiple teams, a regulated estate, or a close that cannot slip during migration.', next: ['Scoping call with an engineer', 'Five-week guided rollout', 'Hand-over to your team'], cta: 'Book a scoping call', variant: 'outline', note: 'Median first reply: 3 h 40 m.', }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-cta-split-panel, [ui-cta-split-panel]', 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, UiSeparatorComponent, ], template: ` <section data-slot="cta-split-panel" [class]="rootClass"> <div class="mx-auto max-w-5xl px-6 py-20 lg:py-28"> <div class="mx-auto max-w-2xl text-center"> <span ui-badge variant="secondary">Two ways in</span> <h2 class="mt-4 text-3xl font-semibold tracking-tight text-balance sm:text-4xl"> Pick the one that matches your situation </h2> </div> <div class="mt-10 grid items-stretch gap-4 md:grid-cols-2"> @for (path of paths; track path.label) { <div ui-card> <div ui-card-content class="flex h-full flex-col p-6"> <span class="border-border text-muted-foreground flex size-10 items-center justify-center rounded-lg border" aria-hidden="true" > <lucide-icon [img]="path.icon" class="size-4" /> </span> <h3 class="mt-4 text-lg font-semibold tracking-tight">{{ path.label }}</h3> <p class="text-muted-foreground mt-2 text-sm leading-relaxed">{{ path.suits }}</p> <ui-separator class="my-5" /> <p class="text-muted-foreground font-mono text-xs tracking-[0.14em] uppercase">What happens next</p> <ol class="mt-3 space-y-2"> @for (step of path.next; track step; let i = $index) { <li class="flex items-start gap-2.5 text-sm"> <span class="text-muted-foreground/70 mt-px font-mono text-xs">{{ i + 1 }}</span> <span>{{ step }}</span> </li> } </ol> <div class="mt-auto pt-6"> <button ui-button [variant]="path.variant" class="w-full"> {{ path.cta }} <lucide-icon [img]="ArrowRight" class="ml-2 size-4" aria-hidden="true" /> </button> <p class="text-muted-foreground mt-2 text-center text-xs">{{ path.note }}</p> </div> </div> </div> } </div> </div> </section> `, }) export class UiCtaSplitPanelComponent { protected readonly ArrowRight = ArrowRight protected readonly paths = paths @Input('class') className?: string get rootClass(): string { return cn('bg-background', this.className) } }src/app/components/blocks/cta-split-panel/index.ts0.1 kB
export { UiCtaSplitPanelComponent } from './cta-split-panel.component'
Raw manifest:https://uipkge.dev/r/angular/cta-split-panel.json