Menu

Change language

GitHub repo

3D Interactive Hero

blockhero

Hero section with hardware-accelerated 3D tilt perspective card, specular light glare, and multi-layer z-depth primitives.

Installation

$npx uipkge-ng@latest add hero-3d-tilt-interactive-canvas
Manifest:https://uipkge.dev/r/angular/hero-3d-tilt-interactive-canvas.jsonInstalls to:src/app/components/blocks/hero-3d-tilt-interactive-canvas/

Variants

Loading interactive previews…

npm dependencies

Includes

Files installed (2)

  • src/app/components/blocks/hero-3d-tilt-interactive-canvas/hero-3d-tilt-interactive-canvas.component.ts8.6 kB
    // Hardware-accelerated 3D perspective hero with dynamic specular reflection and
    // layered z-depth cards. Cursor position drives pitch/yaw rotation and glare.
    // Port of the React block 1:1.
    import { ChangeDetectionStrategy, Component, ElementRef, Input, ViewChild, signal } from '@angular/core'
    import { ArrowRight, Code2, Cpu, Layers, LucideAngularModule, MousePointerClick, Zap } from 'lucide-angular'
    import { cn } from '@/lib/utils'
    import { UiBadgeComponent } from '@/ui/badge/badge.component'
    import { UiButtonComponent } from '@/ui/button/button.component'
    
    export interface Hero3dTiltInteractiveCanvasProps {
      title?: string
      description?: string
      primaryActionLabel?: string
      secondaryActionLabel?: string
    }
    
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-hero-3d-tilt-interactive-canvas, [ui-hero-3d-tilt-interactive-canvas]',
      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],
      template: `
        <section data-slot="hero-3d-tilt-interactive-canvas" [class]="rootClass">
          <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
            <!-- Section Header -->
            <div class="mx-auto max-w-3xl space-y-4 text-center">
              <a
                href="#3d-spatial"
                class="group border-border/80 bg-secondary/60 hover:bg-secondary text-foreground inline-flex items-center gap-2 rounded-full border px-3.5 py-1 text-xs font-medium shadow-2xs transition-colors"
              >
                <lucide-icon [img]="Layers" class="text-primary size-3.5" />
                <span>Spatial 3D Design Engineering</span>
                <lucide-icon
                  [img]="ArrowRight"
                  class="text-muted-foreground size-3 transition-transform group-hover:translate-x-0.5"
                />
              </a>
    
              <h1 class="text-foreground text-4xl font-bold tracking-tight sm:text-5xl lg:text-6xl">
                {{ title }}
              </h1>
    
              <p class="text-muted-foreground text-base sm:text-lg">
                {{ description }}
              </p>
    
              <div class="flex flex-wrap items-center justify-center gap-3 pt-2">
                <button ui-button size="lg" class="gap-2 shadow-xs">
                  <span>{{ primaryActionLabel }}</span>
                  <lucide-icon [img]="ArrowRight" class="size-4" />
                </button>
                <button ui-button variant="outline" size="lg" class="gap-2">
                  <lucide-icon [img]="Code2" class="size-4" />
                  <span>{{ secondaryActionLabel }}</span>
                </button>
              </div>
            </div>
    
            <!-- 3D Perspective Stage Container -->
            <div class="mt-14 flex flex-col items-center justify-center [perspective:1200px]">
              <div
                #card
                [style.transform]="'rotateX(' + rotateX() + 'deg) rotateY(' + rotateY() + 'deg)'"
                [style.transition]="
                  isHovering() ? 'transform 100ms ease-out' : 'transform 500ms cubic-bezier(0.16, 1, 0.3, 1)'
                "
                class="border-border bg-card relative w-full max-w-2xl cursor-crosshair overflow-hidden rounded-2xl border p-8 shadow-xl select-none [transform-style:preserve-3d]"
                (mousemove)="handleMouseMove($event)"
                (mouseenter)="handleMouseEnter()"
                (mouseleave)="handleMouseLeave()"
              >
                <!-- Dynamic Specular Reflection / Glare Gradient -->
                <div
                  [style.background]="
                    'radial-gradient(circle at ' +
                    glareX() +
                    '% ' +
                    glareY() +
                    '%, rgba(255,255,255,0.12) 0%, transparent 60%)'
                  "
                  class="pointer-events-none absolute inset-0 transition-opacity duration-300"
                  [class.opacity-100]="isHovering()"
                  [class.opacity-0]="!isHovering()"
                ></div>
    
                <!-- Layer 1: Base Grid Info -->
                <div class="border-border flex items-center justify-between border-b pb-4">
                  <div class="flex items-center gap-2">
                    <lucide-icon [img]="Layers" class="text-primary size-4" />
                    <span class="text-foreground text-sm font-bold">Spatial Component Viewport</span>
                  </div>
                  <span ui-badge variant="outline" class="border-border text-muted-foreground font-mono text-xs">
                    Pitch: {{ rotateX() }}&deg; &bull; Yaw: {{ rotateY() }}&deg;
                  </span>
                </div>
    
                <!-- Layer 2: Floating 3D Cards & Primitives (Preserve-3d + translateZ) -->
                <div class="mt-8 grid grid-cols-1 gap-4 sm:grid-cols-2">
                  <!-- Floating Badge Card 1 -->
                  <div
                    style="transform: translateZ(35px)"
                    class="border-border/90 bg-muted/40 space-y-2 rounded-xl border p-4 shadow-md transition-transform"
                  >
                    <div class="flex items-center gap-2">
                      <lucide-icon [img]="Cpu" class="text-primary size-4" />
                      <span class="text-foreground text-xs font-bold">GPU Accelerated Matrix</span>
                    </div>
                    <p class="text-muted-foreground text-xs leading-relaxed">
                      Zero JavaScript bundle runtime. Calculated via pure CSS 3D matrix transforms.
                    </p>
                  </div>
    
                  <!-- Floating Badge Card 2 -->
                  <div
                    style="transform: translateZ(50px)"
                    class="border-primary/30 bg-primary/5 space-y-2 rounded-xl border p-4 shadow-lg transition-transform"
                  >
                    <div class="flex items-center gap-2">
                      <lucide-icon [img]="Zap" class="text-success size-4" />
                      <span class="text-foreground text-xs font-bold">Specular Ray Diffusion</span>
                    </div>
                    <p class="text-muted-foreground text-xs leading-relaxed">
                      Dynamic light reflection calculates azimuth angles relative to mouse coordinates.
                    </p>
                  </div>
                </div>
    
                <!-- Bottom Telemetry Bar -->
                <div
                  style="transform: translateZ(25px)"
                  class="border-border/80 bg-background/90 mt-6 flex items-center justify-between rounded-lg border p-3 text-xs"
                >
                  <div class="text-muted-foreground flex items-center gap-2">
                    <lucide-icon [img]="MousePointerClick" class="text-primary size-3.5" />
                    <span>Hover and move cursor to orient viewport</span>
                  </div>
                  <span class="text-foreground font-mono text-xs font-bold">
                    {{ isHovering() ? 'Tracking Live' : 'Hover to Orbit' }}
                  </span>
                </div>
              </div>
            </div>
          </div>
        </section>
      `,
    })
    export class UiHero3dTiltInteractiveCanvasComponent {
      protected readonly ArrowRight = ArrowRight
      protected readonly Code2 = Code2
      protected readonly Cpu = Cpu
      protected readonly Layers = Layers
      protected readonly MousePointerClick = MousePointerClick
      protected readonly Zap = Zap
    
      @Input() title = 'Fluid 3D Spatial Architecture rendered in pure CSS.'
      @Input() description =
        'Experience zero-latency hardware-accelerated 3D perspective transforms with dynamic specular reflections and layered z-depth primitives.'
      @Input() primaryActionLabel = 'Explore 3D Primitives'
      @Input() secondaryActionLabel = 'Copy CSS Matrix Code'
      @Input('class') className?: string
    
      @ViewChild('card') cardRef?: ElementRef<HTMLDivElement>
    
      readonly rotateX = signal(0)
      readonly rotateY = signal(0)
      readonly glareX = signal(50)
      readonly glareY = signal(50)
      readonly isHovering = signal(false)
    
      get rootClass(): string {
        return cn('bg-background relative overflow-hidden py-16 sm:py-24', this.className)
      }
    
      handleMouseMove(event: MouseEvent): void {
        const el = this.cardRef?.nativeElement
        if (!el) return
        const rect = el.getBoundingClientRect()
        const x = event.clientX - rect.left
        const y = event.clientY - rect.top
    
        const centerX = rect.width / 2
        const centerY = rect.height / 2
    
        // Limit max rotation to +/- 14 degrees
        this.rotateY.set(Number(((x - centerX) / (centerX / 14)).toFixed(2)))
        this.rotateX.set(Number((-(y - centerY) / (centerY / 14)).toFixed(2)))
    
        this.glareX.set(Number(((x / rect.width) * 100).toFixed(1)))
        this.glareY.set(Number(((y / rect.height) * 100).toFixed(1)))
      }
    
      handleMouseEnter(): void {
        this.isHovering.set(true)
      }
    
      handleMouseLeave(): void {
        this.isHovering.set(false)
        this.rotateX.set(0)
        this.rotateY.set(0)
      }
    }
    
  • src/app/components/blocks/hero-3d-tilt-interactive-canvas/index.ts0.1 kB

Raw manifest:https://uipkge.dev/r/angular/hero-3d-tilt-interactive-canvas.json