Menu

Change language

GitHub repo

Feature Scroll Spy Walkthrough

blockmarketing

Step-by-step 4-phase developer workflow progression with sticky live code terminal sandbox and instant stage inspector.

Installation

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

Variants

Loading interactive previews…

Schema

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

WalkthroughStep
interface WalkthroughStep {
  id: string
  stepNumber: string
  title: string
  subtitle: string
  description: string
  terminalOutput: string
  badgeText: string
  meta: string
}

npm dependencies

Includes

Files installed (2)

  • src/app/components/blocks/feature-scroll-spy-walkthrough/feature-scroll-spy-walkthrough.component.ts9.1 kB
    // 4-phase developer workflow walkthrough: clickable step cards on the left and a
    // sticky live terminal sandbox on the right with prev/next progression. Port of the
    // React block 1:1.
    import { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'
    import { LucideAngularModule, Rocket } from 'lucide-angular'
    import { cn } from '@/lib/utils'
    import { UiBadgeComponent } from '@/ui/badge/badge.component'
    import { UiCardComponent } from '@/ui/card/card.component'
    
    export interface WalkthroughStep {
      id: string
      stepNumber: string
      title: string
      subtitle: string
      description: string
      terminalOutput: string
      badgeText: string
      meta: string
    }
    
    const steps: WalkthroughStep[] = [
      {
        id: 'init',
        stepNumber: '01',
        title: 'Initialize Registry Foundation',
        subtitle: 'Zero configuration Tailwind tokens',
        description:
          'Run the init command to pull OKLCH color palettes, calibrated shadow tokens, spring physics curves, and cn() utility functions into your project.',
        terminalOutput: `$ npx shadcn@latest add https://uipkge.dev/r/react/init.json -y\n✔ Downloaded styles/tailwind.css (OKLCH tokens)\n✔ Injected lib/utils.ts (cn helper)\n✔ Configured @/ alias path mapping`,
        badgeText: 'Instant Scaffolding',
        meta: 'Execution time: 1.4s',
      },
      {
        id: 'primitives',
        stepNumber: '02',
        title: 'Pull Headless Primitives',
        subtitle: 'Radix UI powered primitives',
        description:
          'Pull accessible, keyboard-friendly primitives like Button, Card, Badge, and Sparklines directly into components/ui/ with full TypeScript type safety.',
        terminalOutput: `$ npx shadcn@latest add @uipkge/button @uipkge/card -y\n✔ Created components/ui/button/Button.tsx\n✔ Created components/ui/button/button.variants.ts\n✔ Zero npm package dependencies added`,
        badgeText: 'AST Injection',
        meta: 'Unbundled & Tree-shaken',
      },
      {
        id: 'compose',
        stepNumber: '03',
        title: 'Compose Rich Workbenches',
        subtitle: 'Visible markup with zero hidden templates',
        description:
          'Compose tiles, headers, and interactive state directly in your pages. No hidden wrapper abstractions or rigid data prop constraints.',
        terminalOutput: `// In your React 19 JSX:\nimport { Button } from '@/components/ui/button'\nimport { Card } from '@/components/ui/card'\n\nexport function Workbench() {\n  return <Card className="p-6">...</Card>\n}`,
        badgeText: 'Raw Composition',
        meta: '100% Code Ownership',
      },
      {
        id: 'deploy',
        stepNumber: '04',
        title: 'Ship to Global Edge',
        subtitle: 'Zero runtime overhead',
        description:
          'Deploy to Cloudflare, Vercel, or AWS with sub-millisecond cold starts and pristine 100/100 Core Web Vitals performance.',
        terminalOutput: `$ npm run build\n✔ Client build: 38.4 kB (Gzip: 11.2 kB)\n✔ Lighthouse Performance: 100 / 100\n✔ First Contentful Paint: 0.4s\n🚀 Deployed to Edge CDN`,
        badgeText: 'Production Ready',
        meta: 'Zero CSS-in-JS Runtime',
      },
    ]
    
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-feature-scroll-spy-walkthrough, [ui-feature-scroll-spy-walkthrough]',
      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-scroll-spy-walkthrough" [class]="rootClass">
          <div class="mx-auto max-w-6xl 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]="Rocket" class="text-primary size-3.5" />
                End-to-End Developer Journey
              </span>
              <h2 class="text-foreground text-3xl font-bold tracking-tight sm:text-4xl">
                From first command to global production in minutes.
              </h2>
              <p class="text-muted-foreground text-base">
                Follow the 4-stage unbundled workflow that gives your team total freedom and zero maintenance fatigue.
              </p>
            </div>
    
            <!-- Interactive 2-Column Split: Steps Rail on Left, Sticky Live Sandbox on Right -->
            <div class="grid grid-cols-1 items-start gap-8 lg:grid-cols-12">
              <!-- Left: Step Navigation Cards (5 Cols) -->
              <div class="space-y-3 lg:col-span-5">
                @for (st of allSteps; track st.id; let idx = $index) {
                  <div [class]="stepCardClass(idx)" (click)="activeStepIndex.set(idx)">
                    <div class="flex items-start gap-4">
                      <div [class]="stepNumberClass(idx)">
                        {{ st.stepNumber }}
                      </div>
    
                      <div class="min-w-0 flex-1 space-y-1">
                        <div class="flex items-center justify-between">
                          <h3 class="text-foreground truncate font-mono text-sm font-bold">{{ st.title }}</h3>
                          <span ui-badge variant="outline" class="ml-2 shrink-0 font-mono text-xs">
                            {{ st.badgeText }}
                          </span>
                        </div>
                        <p class="text-muted-foreground text-xs leading-relaxed">{{ st.description }}</p>
                      </div>
                    </div>
                  </div>
                }
              </div>
    
              <!-- Right: Sticky Live Sandbox Terminal Display (7 Cols) -->
              <div
                ui-card
                class="border-border bg-card/95 sticky top-24 space-y-6 rounded-2xl p-6 text-left shadow-sm sm:p-8 lg:col-span-7"
              >
                <!-- Terminal Header Bar -->
                <div class="border-border flex items-center justify-between border-b pb-4">
                  <div class="flex items-center gap-2">
                    <div class="bg-destructive/80 size-3 rounded-full"></div>
                    <div class="bg-warning/80 size-3 rounded-full"></div>
                    <div class="bg-success/80 size-3 rounded-full"></div>
                    <span class="text-muted-foreground ml-2 font-mono text-xs">
                      stage_{{ currentStep().stepNumber }}_{{ currentStep().id }}.sh
                    </span>
                  </div>
                  <span ui-badge variant="secondary" class="border-success/20 bg-success/10 text-success font-mono text-xs">
                    {{ currentStep().meta }}
                  </span>
                </div>
    
                <!-- Code Sandbox Body -->
                <div class="space-y-2">
                  <pre
                    class="border-border bg-muted/40 text-foreground overflow-x-auto rounded-xl border p-5 font-mono text-xs leading-relaxed whitespace-pre-wrap"
                  ><code>{{ currentStep().terminalOutput }}</code></pre>
                </div>
    
                <!-- Step Progression Navigator -->
                <div class="border-border flex items-center justify-between border-t pt-2 font-mono text-xs">
                  <button
                    type="button"
                    class="text-muted-foreground hover:text-foreground disabled:pointer-events-none disabled:opacity-30"
                    [disabled]="activeStepIndex() === 0"
                    (click)="prevStep()"
                  >
                    &larr; Previous Step
                  </button>
    
                  <span class="text-muted-foreground">Step {{ activeStepIndex() + 1 }} of {{ allSteps.length }}</span>
    
                  <button
                    type="button"
                    class="text-primary font-semibold hover:underline disabled:pointer-events-none disabled:opacity-30"
                    [disabled]="activeStepIndex() === allSteps.length - 1"
                    (click)="nextStep()"
                  >
                    Next Step &rarr;
                  </button>
                </div>
              </div>
            </div>
          </div>
        </section>
      `,
    })
    export class UiFeatureScrollSpyWalkthroughComponent {
      protected readonly Rocket = Rocket
      protected readonly allSteps = steps
    
      @Input() set initialStep(v: number) {
        if (Number.isInteger(v) && v >= 0 && v < steps.length) this.activeStepIndex.set(v)
      }
      @Input('class') className?: string
    
      readonly activeStepIndex = signal(0)
    
      readonly currentStep = computed(() => steps[this.activeStepIndex()] ?? steps[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)
      }
    
      stepCardClass(idx: number): string {
        return cn(
          'group relative cursor-pointer overflow-hidden rounded-2xl border p-5 text-left transition-colors',
          this.activeStepIndex() === idx
            ? 'border-primary/80 bg-card ring-primary/20 shadow-lg ring-1'
            : 'border-border/70 bg-card/40 hover:border-border hover:bg-card/70',
        )
      }
    
      stepNumberClass(idx: number): string {
        return cn(
          'flex size-8 shrink-0 items-center justify-center rounded-lg font-mono text-xs font-bold transition-colors',
          this.activeStepIndex() === idx
            ? 'bg-primary text-primary-foreground shadow-xs'
            : 'bg-muted text-muted-foreground',
        )
      }
    
      prevStep(): void {
        this.activeStepIndex.update((i) => Math.max(0, i - 1))
      }
    
      nextStep(): void {
        this.activeStepIndex.update((i) => Math.min(steps.length - 1, i + 1))
      }
    }
    
  • src/app/components/blocks/feature-scroll-spy-walkthrough/index.ts0.1 kB

Raw manifest:https://uipkge.dev/r/angular/feature-scroll-spy-walkthrough.json