Menu

Change language

GitHub repo

Feature Interactive Tabs

blockmarketing

Master-detail architectural feature workbench with interactive source snippets, copy action, telemetry stats, and verified technical checks.

Installation

$npx uipkge-ng@latest add feature-interactive-tabs
Manifest:https://uipkge.dev/r/angular/feature-interactive-tabs.jsonInstalls to:src/app/components/blocks/feature-interactive-tabs/

Variants

Loading interactive previews…

Schema

Type aliases exported from this item's source. Use these to shape the data you pass in.

FeatureTab
interface FeatureTab {
  id: string
  title: string
  subtitle: string
  icon: LucideIconData
  tag: string
  headline: string
  description: string
  codeSnippet: string
  stats: { label: string; value: string }[]
  checks: string[]
}

npm dependencies

Includes

Files installed (2)

  • src/app/components/blocks/feature-interactive-tabs/feature-interactive-tabs.component.ts13.4 kB
    // Master-detail architectural feature workbench: a navigation rail of feature tabs on
    // the left and an interactive detail sandbox (tag, headline, copyable source snippet,
    // telemetry stats, verified checks) on the right. Port of the React block 1:1.
    import { ChangeDetectionStrategy, Component, DestroyRef, Input, computed, inject, signal } from '@angular/core'
    import {
      Activity,
      Check,
      CheckCircle2,
      Code2,
      Copy,
      LayoutGrid,
      LucideAngularModule,
      Terminal,
      Zap,
      type LucideIconData,
    } from 'lucide-angular'
    import { cn } from '@/lib/utils'
    import { UiBadgeComponent } from '@/ui/badge/badge.component'
    import { UiCardComponent } from '@/ui/card/card.component'
    
    export interface FeatureTab {
      id: string
      title: string
      subtitle: string
      icon: LucideIconData
      tag: string
      headline: string
      description: string
      codeSnippet: string
      stats: { label: string; value: string }[]
      checks: string[]
    }
    
    const tabs: FeatureTab[] = [
      {
        id: 'unbundled',
        title: 'Zero-Package Registry',
        subtitle: 'Direct AST source ownership',
        icon: Code2,
        tag: 'No Node Modules Bloat',
        headline: 'You own 100% of the source code. No semver breaking changes.',
        description:
          'Unlike monolithic component packages that lock you into inflexible dependency trees, UIPKGE copies pristine, tree-shakeable source files directly into your project repository.',
        codeSnippet: `// Terminal command:\nnpx shadcn@latest add https://uipkge.dev/r/react/hero-developer-terminal.json\n\n// Output in your repo:\n// ✔ Placed components/blocks/HeroDeveloperTerminal.tsx\n// ✔ Installed zero runtime wrapper overhead`,
        stats: [
          { label: 'Bundle Size Overhead', value: '0.0 kB' },
          { label: 'Semver Breakage Risk', value: '0%' },
          { label: 'Customization Freedom', value: '100%' },
        ],
        checks: [
          'Raw SFC/TSX files living right in your /components directory',
          'Edit Tailwind tokens, props, or behavior anytime without forking',
          'Never wait on upstream maintainers for critical hotfixes or styling tweaks',
        ],
      },
      {
        id: 'parity',
        title: 'Dual-Framework Parity',
        subtitle: 'Synchronized Vue 3.5 & React 19',
        icon: LayoutGrid,
        tag: '1:1 AST Parity',
        headline: 'One design system, identical aesthetics across Vue and React.',
        description:
          'Whether your engineering organization uses Nuxt 3, Vite, Next.js, or Astro, every primitive and block shares identical OKLCH color spaces, CVA variant tokens, and spring transition curves.',
        codeSnippet: `// Vue 3.5 SFC:\n<Button variant="default" size="sm">Deploy</Button>\n\n// React 19 JSX:\n<Button variant="default" size="sm">Deploy</Button>\n\n// Both generate identical DOM attributes & micro-interactions`,
        stats: [
          { label: 'Token Parity', value: '100.0%' },
          { label: 'Supported Frameworks', value: 'Vue 3.5 + React 19' },
          { label: 'Shared CVA Tokens', value: '31 Primitives' },
        ],
        checks: [
          'Reka UI (Vue) and Radix UI (React) accessible headless engines',
          'Automated CI check verifying cross-framework class string equality',
          'Identical behavior in Vue and React microfrontends',
        ],
      },
      {
        id: 'telemetry',
        title: 'Interactive Workbenches',
        subtitle: 'Beyond static cards & fluff',
        icon: Activity,
        tag: 'Dense & Functional',
        headline: 'Real-time controls, telemetry, and live interactive state.',
        description:
          'Every UIPKGE marketing block functions as a live interactive workbench with stateful sliders, timeframe toggles, and reactive calculation engines.',
        codeSnippet: `// Composing live reactive telemetry in your blocks:\nconst [activeCohort, setActiveCohort] = React.useState<'enterprise' | 'growth'>('enterprise')\nconst liveThroughput = activeCohort === 'enterprise' ? '1.2M QPS' : '45k QPS'`,
        stats: [
          { label: 'Interactive Demos', value: '450+ Blocks' },
          { label: 'Keyboard Ergonomics', value: 'WCAG AA' },
          { label: 'Telemetry Visualizers', value: 'Real SVG Sparklines' },
        ],
        checks: [
          'Functional master-detail views and comparative toggles',
          'Integrated mock API testers, terminal sandboxes, and pricing calculators',
          'Zero placeholder text or dummy lorum ipsum shapes',
        ],
      },
      {
        id: 'performance',
        title: 'Sub-Millisecond Speed',
        subtitle: 'OKLCH Tailwind v4 Engine',
        icon: Zap,
        tag: 'Lighthouse 100',
        headline: 'Engineered for zero-latency interactions and pristine Lighthouse scores.',
        description:
          'Built on modern CSS custom properties and lightweight headless primitives, our blocks avoid hefty client-side runtime libraries, keeping your First Contentful Paint blazing fast.',
        codeSnippet: `@theme inline {\n  --color-primary: oklch(0.205 0 0);\n  --color-primary-foreground: oklch(0.985 0 0);\n  --ease-spring: cubic-bezier(0.16, 1, 0.3, 1);\n}`,
        stats: [
          { label: 'Interaction to Next Paint (INP)', value: '< 16ms' },
          { label: 'Lighthouse Score', value: '100 / 100' },
          { label: 'CSS Engine', value: 'Tailwind CSS v4' },
        ],
        checks: [
          'OKLCH native color spaces with automatic light/dark mode adaptation',
          'Hardware-accelerated transforms and calibrated spring physics',
          'Zero runtime JavaScript CSS parsing or CSS-in-JS style injection',
        ],
      },
    ]
    
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-feature-interactive-tabs, [ui-feature-interactive-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, UiCardComponent],
      template: `
        <section data-slot="feature-interactive-tabs" [class]="rootClass">
          <!-- Subtle Radial Glow -->
          <div
            class="bg-primary/5 pointer-events-none absolute top-1/2 left-1/2 -z-10 h-80 w-full max-w-5xl -translate-x-1/2 -translate-y-1/2 rounded-full blur-xl"
          ></div>
    
          <div class="mx-auto max-w-7xl space-y-12">
            <!-- Section Header -->
            <div class="mx-auto max-w-3xl space-y-4 text-center">
              <span ui-badge variant="secondary" class="gap-1.5 px-3 py-1 font-mono text-xs shadow-xs">
                <lucide-icon [img]="LayoutGrid" class="text-primary size-3.5" />
                Architecture Differentiation
              </span>
              <h2 class="text-foreground text-3xl font-bold tracking-tight sm:text-4xl">
                Why engineering teams choose UIPKGE.
              </h2>
              <p class="text-muted-foreground text-base">
                Explore the architectural pillars behind our unbundled component registry.
              </p>
            </div>
    
            <!-- Feature Interactive Tabs Workbench -->
            <div class="grid grid-cols-1 items-start gap-6 lg:grid-cols-12">
              <!-- Left Column: Master Feature Navigation Rail (4 Cols) -->
              <div class="flex flex-col gap-2.5 lg:col-span-4">
                @for (tab of allTabs; track tab.id) {
                  <button type="button" [class]="railButtonClass(tab.id)" (click)="setActiveTab(tab.id)">
                    <span class="flex items-start gap-3.5">
                      <span [class]="railIconClass(tab.id)">
                        <lucide-icon [img]="tab.icon" class="size-4" />
                      </span>
    
                      <span class="min-w-0 space-y-0.5">
                        <span class="flex items-center justify-between gap-2">
                          <span class="text-foreground truncate font-mono text-xs font-bold">{{ tab.title }}</span>
                          <span
                            [class]="
                              cn(
                                'size-1.5 shrink-0 rounded-full',
                                activeTabId() === tab.id ? 'bg-primary animate-pulse' : 'bg-transparent'
                              )
                            "
                          ></span>
                        </span>
                        <span class="text-muted-foreground block truncate text-xs">{{ tab.subtitle }}</span>
                      </span>
                    </span>
                  </button>
                }
              </div>
    
              <!-- Right Column: Interactive Detail Sandbox Panel (8 Cols) -->
              <div
                ui-card
                class="border-border bg-card/95 space-y-6 rounded-2xl p-6 text-left shadow-sm sm:p-8 lg:col-span-8"
              >
                <!-- Detail Header -->
                <div class="border-border/80 space-y-3 border-b pb-6">
                  <div class="flex flex-wrap items-center justify-between gap-2">
                    <span ui-badge variant="outline" class="text-primary bg-primary/5 border-primary/20 font-mono text-xs">
                      {{ currentTab().tag }}
                    </span>
                    <span class="text-muted-foreground font-mono text-xs">Architectural Specification</span>
                  </div>
                  <h3 class="text-foreground text-xl font-bold tracking-tight sm:text-2xl">
                    {{ currentTab().headline }}
                  </h3>
                  <p class="text-muted-foreground text-xs leading-relaxed sm:text-sm">
                    {{ currentTab().description }}
                  </p>
                </div>
    
                <!-- Code Snippet Sandbox Preview -->
                <div class="space-y-2">
                  <div class="text-muted-foreground flex items-center justify-between font-mono text-xs">
                    <span class="flex items-center gap-1.5">
                      <lucide-icon [img]="Terminal" class="text-primary size-3.5" /> Source Implementation
                    </span>
                    <button
                      type="button"
                      class="text-primary flex items-center gap-1 hover:underline"
                      (click)="copySnippet()"
                    >
                      @if (copiedCode()) {
                        <lucide-icon [img]="Check" class="text-success size-3" />
                      } @else {
                        <lucide-icon [img]="Copy" class="size-3" />
                      }
                      <span>{{ copiedCode() ? 'Copied' : 'Copy Snippet' }}</span>
                    </button>
                  </div>
                  <pre
                    class="border-border bg-muted/40 text-foreground overflow-x-auto rounded-xl border p-4 font-mono text-xs leading-relaxed"
                  ><code>{{ currentTab().codeSnippet }}</code></pre>
                </div>
    
                <!-- 3-Pillar Telemetry Metrics Bar -->
                <div class="grid grid-cols-1 gap-3 pt-2 sm:grid-cols-3">
                  @for (st of currentTab().stats; track st.label) {
                    <div class="border-border bg-background/50 space-y-0.5 rounded-xl border p-3.5">
                      <p class="text-muted-foreground font-mono text-xs tracking-wider uppercase">{{ st.label }}</p>
                      <p class="text-foreground font-mono text-base font-bold">{{ st.value }}</p>
                    </div>
                  }
                </div>
    
                <!-- Technical Checkmarks List -->
                <div class="border-border/60 space-y-2 border-t pt-2">
                  <p class="text-muted-foreground font-mono text-xs tracking-wider uppercase">Verified Enforcements</p>
                  <ul class="space-y-2">
                    @for (chk of currentTab().checks; track chk) {
                      <li class="text-foreground/90 flex items-start gap-2 text-xs">
                        <lucide-icon [img]="CheckCircle2" class="text-success mt-0.5 size-4 shrink-0" />
                        <span>{{ chk }}</span>
                      </li>
                    }
                  </ul>
                </div>
              </div>
            </div>
          </div>
        </section>
      `,
    })
    export class UiFeatureInteractiveTabsComponent {
      protected readonly cn = cn
      protected readonly Check = Check
      protected readonly CheckCircle2 = CheckCircle2
      protected readonly Copy = Copy
      protected readonly LayoutGrid = LayoutGrid
      protected readonly Terminal = Terminal
    
      protected readonly allTabs = tabs
    
      @Input() set initialTab(v: string) {
        if (tabs.some((t) => t.id === v)) this.activeTabId.set(v)
      }
      @Input('class') className?: string
    
      readonly activeTabId = signal('unbundled')
      readonly copiedCode = signal(false)
      private copyTimer: ReturnType<typeof setTimeout> | undefined
    
      constructor() {
        inject(DestroyRef).onDestroy(() => clearTimeout(this.copyTimer))
      }
    
      readonly currentTab = computed(() => tabs.find((t) => t.id === this.activeTabId()) ?? tabs[0]!)
    
      get rootClass(): string {
        return cn('bg-background relative overflow-hidden px-4 py-16 sm:px-6 sm:py-24 lg:px-8', this.className)
      }
    
      railButtonClass(id: string): string {
        return cn(
          'group relative overflow-hidden rounded-2xl border p-4 text-left transition-[colors,box-shadow,transform] duration-150 active:scale-[0.98]',
          this.activeTabId() === id
            ? 'border-primary/80 bg-card ring-primary/20 shadow-sm ring-1'
            : 'border-border bg-card/60 hover:border-border hover:bg-card',
        )
      }
    
      railIconClass(id: string): string {
        return cn(
          'flex size-9 shrink-0 items-center justify-center rounded-lg font-mono transition-colors',
          this.activeTabId() === id
            ? 'bg-primary text-primary-foreground font-bold shadow-xs'
            : 'bg-muted text-muted-foreground group-hover:text-foreground',
        )
      }
    
      setActiveTab(id: string): void {
        this.activeTabId.set(id)
      }
    
      async copySnippet(): Promise<void> {
        try {
          await navigator.clipboard.writeText(this.currentTab().codeSnippet)
          this.copiedCode.set(true)
          clearTimeout(this.copyTimer)
          this.copyTimer = setTimeout(() => this.copiedCode.set(false), 2000)
        } catch {
          // Clipboard unavailable (e.g. insecure context): leave the label unchanged.
        }
      }
    }
    
  • src/app/components/blocks/feature-interactive-tabs/index.ts0.1 kB

Raw manifest:https://uipkge.dev/r/angular/feature-interactive-tabs.json