
3D Extruded Buildings & Urban Footprints
Real-world 3D building extrusions with dynamic sunlight shadows, terrain DEM elevations, and pitch/bearing camera controls.
Hero section collection with eleven interchangeable variants: metric showcase, 3D tilt canvas, ASCII terminal stream, bento preview grid, developer terminal, interactive globe nodes, SaaS metrics dashboard, split device mockup, typing command center, video walkthrough modal, and glowing waitlist.
Also available for React ->$pnpm dlx shadcn-vue@latest add https://uipkge.dev/r/vue/hero.json$npx shadcn-vue@latest add https://uipkge.dev/r/vue/hero.json$yarn dlx shadcn-vue@latest add https://uipkge.dev/r/vue/hero.json$bunx shadcn-vue@latest add https://uipkge.dev/r/vue/hero.jsonnpx shadcn-vue@latest add @uipkge/heroInstalls to:app/components/blocks/hero/Type aliases exported from this item's source. Use these to shape the data you pass in.
CommandPresetinterface CommandPreset {
id: string
label: string
command: string
output: string[]
}EdgeNodeinterface EdgeNode {
id: string
name: string
region: string
country: string
city: string
latencyMs: number
p99LatencyMs: number
cacheHitRatio: number
qps: number
status: 'optimal' | 'operational' | 'degraded'
coordinates: { x: number; y: number }
}MetricDatainterface MetricData {
mrr: string
growth: string
activeUsers: string
requests: string
chartPoints: number[]
}CommandActioninterface CommandAction {
id: string
title: string
category: 'Components' | 'Themes' | 'Git' | 'Workflows'
shortcut: string[]
icon: any
description: string
status: string
}VideoChapterinterface VideoChapter {
id: string
title: string
timestamp: string
description: string
}RoleOptioninterface RoleOption {
id: Role
label: string
perk: string
}CSS custom properties referenced in this item. Override any of them in your :root or per-element to retheme.
--primary<script setup lang="ts">
import { ArrowRight, PlayCircle } from 'lucide-vue-next'
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
</script>
<template>
<section data-slot="hero-01" class="bg-background relative overflow-hidden">
<div class="relative mx-auto max-w-6xl px-6 py-24 lg:py-32">
<div class="grid gap-12 lg:grid-cols-2 lg:items-center">
<div class="space-y-6">
<h1 class="text-4xl font-semibold tracking-tight sm:text-5xl lg:text-6xl">
The platform your team will actually use.
</h1>
<p class="text-muted-foreground max-w-xl text-lg">
One workspace for everything your team needs. Built on shadcn-vue primitives — fast, accessible, easy to
customise.
</p>
<div class="flex flex-wrap items-center gap-3">
<Button size="lg">
Start free trial
<ArrowRight class="ml-2 size-4" />
</Button>
<Button size="lg" variant="outline">
<PlayCircle class="mr-2 size-4" />
Watch demo (2 min)
</Button>
</div>
<div class="text-muted-foreground flex flex-wrap items-center gap-x-6 gap-y-2 text-sm">
<span>★★★★★ 4.9 on G2</span>
<span>14-day free trial</span>
<span>No credit card required</span>
</div>
</div>
<div class="relative">
<Card class="absolute -top-4 right-0 w-56 rotate-2 shadow-lg">
<CardContent class="space-y-1 p-4">
<p class="text-muted-foreground text-xs uppercase">Active users</p>
<p class="text-2xl font-semibold">1,284</p>
<p class="text-success text-xs">+8.2% MoM</p>
</CardContent>
</Card>
<Card class="absolute top-32 -left-2 w-52 -rotate-3 shadow-lg">
<CardContent class="space-y-1 p-4">
<p class="text-muted-foreground text-xs uppercase">Uptime</p>
<p class="text-2xl font-semibold">100%</p>
<p class="text-muted-foreground text-xs">12 cycles, 0 misses</p>
</CardContent>
</Card>
<Card class="ml-auto w-72 shadow-xl">
<CardHeader>
<CardTitle class="text-base">Onboarding queue</CardTitle>
<CardDescription>3 starting Monday</CardDescription>
</CardHeader>
<CardContent class="space-y-3">
<div class="flex items-center gap-3">
<Avatar class="size-8">
<AvatarFallback>LW</AvatarFallback>
</Avatar>
<div class="flex-1">
<p class="text-sm font-medium">Lena Wei</p>
<p class="text-muted-foreground text-xs">Engineering</p>
</div>
<Badge variant="secondary">Pending</Badge>
</div>
<div class="flex items-center gap-3">
<Avatar class="size-8">
<AvatarFallback>JR</AvatarFallback>
</Avatar>
<div class="flex-1">
<p class="text-sm font-medium">Joaquín Reyes</p>
<p class="text-muted-foreground text-xs">Engineering</p>
</div>
<Badge variant="secondary">Pending</Badge>
</div>
<div class="flex items-center gap-3">
<Avatar class="size-8">
<AvatarFallback>PS</AvatarFallback>
</Avatar>
<div class="flex-1">
<p class="text-sm font-medium">Priya Shah</p>
<p class="text-muted-foreground text-xs">Engineering</p>
</div>
<Badge variant="secondary">Pending</Badge>
</div>
</CardContent>
</Card>
</div>
</div>
</div>
</section>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { ArrowRight, Code2, Cpu, Layers, MousePointerClick, Zap } from 'lucide-vue-next'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { cn } from '@/lib/utils'
export interface Hero3dTiltInteractiveCanvasProps {
title?: string
description?: string
primaryActionLabel?: string
secondaryActionLabel?: string
class?: string
}
const props = withDefaults(defineProps<Hero3dTiltInteractiveCanvasProps>(), {
title: 'Fluid 3D Spatial Architecture rendered in pure CSS.',
description:
'Experience zero-latency hardware-accelerated 3D perspective transforms with dynamic specular reflections and layered z-depth primitives.',
primaryActionLabel: 'Explore 3D Primitives',
secondaryActionLabel: 'Copy CSS Matrix Code',
})
const cardRef = ref<HTMLDivElement | null>(null)
const rotateX = ref(0)
const rotateY = ref(0)
const glareX = ref(50)
const glareY = ref(50)
const isHovering = ref(false)
function handleMouseMove(e: MouseEvent) {
if (!cardRef.value) return
const rect = cardRef.value.getBoundingClientRect()
const x = e.clientX - rect.left
const y = e.clientY - rect.top
const centerX = rect.width / 2
const centerY = rect.height / 2
// Limit max rotation to +/- 14 degrees
rotateY.value = Number(((x - centerX) / (centerX / 14)).toFixed(2))
rotateX.value = Number((-(y - centerY) / (centerY / 14)).toFixed(2))
glareX.value = Number(((x / rect.width) * 100).toFixed(1))
glareY.value = Number(((y / rect.height) * 100).toFixed(1))
}
function handleMouseEnter() {
isHovering.value = true
}
function handleMouseLeave() {
isHovering.value = false
rotateX.value = 0
rotateY.value = 0
}
</script>
<template>
<section
data-slot="hero-3d-tilt-interactive-canvas"
:class="cn('bg-background relative overflow-hidden py-16 sm:py-24', props.class)"
>
<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"
>
<Layers class="text-primary size-3.5" />
<span>Spatial 3D Design Engineering</span>
<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 size="lg" class="gap-2 shadow-xs">
<span>{{ primaryActionLabel }}</span>
<ArrowRight class="size-4" />
</Button>
<Button variant="outline" size="lg" class="gap-2">
<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
ref="cardRef"
:style="{
transform: `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`,
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"
@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="isHovering ? 'opacity-100' : 'opacity-0'"
/>
<!-- 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">
<Layers class="text-primary size-4" />
<span class="text-foreground text-sm font-bold">Spatial Component Viewport</span>
</div>
<Badge variant="outline" class="border-border text-muted-foreground font-mono text-xs">
Pitch: {{ rotateX }}° • Yaw: {{ rotateY }}°
</Badge>
</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">
<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">
<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">
<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>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { ArrowRight, Terminal } from 'lucide-vue-next'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
import { cn } from '@/lib/utils'
export interface HeroAsciiTerminalStreamProps {
title?: string
description?: string
class?: string
}
const ASCII_BANNER = `
_ _ ___ ____ _ ______ _____
| | | |_ _| _ \\| |/ / ___| ____|
| | | || || |_) | ' / | _| _|
| |_| || || __/| . \\ |_| | |___
\\___/|___|_| |_|\\_\\____|_____|
================================
DUAL-FRAMEWORK UNBUNDLED REGISTRY
`
const props = withDefaults(defineProps<HeroAsciiTerminalStreamProps>(), {
title: 'Deterministic UI engineering stream for technical operators.',
description:
'Inspect build outputs, verify zero-dependency bundle telemetry, and stream component ASTs in real time with hardware precision.',
})
const terminalLogs = ref<string[]>([
'INIT: Initializing UIPKGE dual-framework AST registry...',
'RESOLVE: Synchronizing @uipkge/button -> Vue 3.5 & React 19',
'OKLCH: Loaded 48 semantic color variables from packages/shared',
'BENCHMARK: Cold compilation completed in 1.42ms (zero dead code)',
'READY: Terminal listening on edge:8080',
])
const inputCommand = ref('')
const isRunning = ref(false)
function runCommand(cmd: string) {
const cleanCmd = cmd.trim().toLowerCase()
if (!cleanCmd) return
terminalLogs.value.push(`$ ${cleanCmd}`)
isRunning.value = true
setTimeout(() => {
if (cleanCmd === 'clear') {
terminalLogs.value = ['TERMINAL: Buffer cleared.']
} else if (cleanCmd === 'bench') {
terminalLogs.value.push('BENCHMARK: Button (1.1kb) | Modal (2.8kb) | DataGrid (4.2kb)')
terminalLogs.value.push('RESULT: 100% Tree-shaken bundle efficiency.')
} else if (cleanCmd === 'parity') {
terminalLogs.value.push('PARITY: 31 variant tokens evaluated across Vue & React.')
terminalLogs.value.push('STATUS: [PASS] Zero framework divergence detected.')
} else if (cleanCmd === 'tree') {
terminalLogs.value.push('TREE: packages/registry-vue (471) <-> packages/registry-react (471)')
} else {
terminalLogs.value.push(`EXEC: Command '${cleanCmd}' executed with exit code 0.`)
}
isRunning.value = false
}, 400)
inputCommand.value = ''
}
</script>
<template>
<section
data-slot="hero-ascii-terminal-stream"
:class="cn('bg-background relative overflow-hidden py-16 sm:py-24', props.class)"
>
<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="#ascii-terminal"
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"
>
<Terminal class="text-primary size-3.5" />
<span>Real-time ASCII Telemetry Stream</span>
<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>
<!-- Command Chips -->
<div class="flex flex-wrap items-center justify-center gap-2 pt-2">
<span class="text-muted-foreground text-xs">Quick commands:</span>
<button
v-for="chip in ['bench', 'parity', 'tree', 'clear']"
:key="chip"
type="button"
class="border-border bg-muted/40 hover:bg-muted text-foreground rounded border px-2 py-0.5 font-mono text-xs transition-colors"
@click="runCommand(chip)"
>
${{ chip }}
</button>
</div>
</div>
<!-- Main ASCII Terminal Card -->
<div class="mx-auto mt-12 max-w-4xl">
<Card class="border-border bg-card overflow-hidden font-mono text-xs shadow-lg">
<!-- Terminal Title Bar -->
<div class="border-border bg-muted/40 flex items-center justify-between border-b px-4 py-2.5">
<div class="flex items-center gap-2">
<div class="flex gap-1.5">
<span class="bg-destructive/80 size-2.5 rounded-full" />
<span class="bg-warning/80 size-2.5 rounded-full" />
<span class="bg-success/80 size-2.5 rounded-full" />
</div>
<span class="text-foreground pl-2 text-xs font-bold">uipkge-telemetry-cli • zsh</span>
</div>
<div class="text-muted-foreground flex items-center gap-3 text-xs">
<span class="flex items-center gap-1">
<span class="bg-success size-1.5 rounded-full" />
<span>60 FPS</span>
</span>
<span>1.2MB RSS</span>
</div>
</div>
<!-- Terminal Content Viewport -->
<CardContent class="bg-background/95 space-y-4 p-6">
<!-- ASCII Art Banner -->
<pre
class="text-primary overflow-x-auto text-xs leading-tight font-bold select-none"
><code>{{ ASCII_BANNER }}</code></pre>
<!-- Logs Stream Output -->
<div class="border-border/80 max-h-56 space-y-1.5 overflow-y-auto border-t pt-2 text-xs">
<div
v-for="(log, idx) in terminalLogs"
:key="idx"
:class="
cn(
'leading-relaxed',
log.startsWith('$')
? 'text-primary font-bold'
: log.includes('PASS') || log.includes('OKLCH')
? 'text-success'
: 'text-muted-foreground',
)
"
>
{{ log }}
</div>
</div>
<!-- Terminal Interactive Input Prompt -->
<form
class="border-border flex items-center gap-2 border-t pt-3"
@submit.prevent="runCommand(inputCommand)"
>
<span class="text-primary font-bold">></span>
<input
v-model="inputCommand"
type="text"
placeholder="Type command (e.g. bench, parity, clear)..."
class="text-foreground placeholder:text-muted-foreground flex-1 bg-transparent text-xs focus-visible:outline-none"
/>
<Button size="sm" type="submit" class="h-7 gap-1 px-2.5 text-xs">
<span>Run</span>
<ArrowRight class="size-3" />
</Button>
</form>
</CardContent>
</Card>
</div>
</div>
</section>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { Activity, ArrowRight, CheckCircle2, Code2, Cpu, Layers, Moon, Palette, Play, Sun, Zap } from 'lucide-vue-next'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
// Tile 1: OKLCH Palette preview state
const colorThemes = [
{ name: 'Emerald', hue: 'oklch(0.65 0.22 145)', hex: '#10b981' },
{ name: 'Indigo', hue: 'oklch(0.60 0.24 275)', hex: '#6366f1' },
{ name: 'Amber', hue: 'oklch(0.75 0.18 70)', hex: '#f59e0b' },
{ name: 'Rose', hue: 'oklch(0.65 0.24 15)', hex: '#f43f5e' },
]
const activeColorIndex = ref(0)
const isDarkPreview = ref(true)
// Tile 2: Dual framework parity toggle
const activeFramework = ref<'vue' | 'react'>('vue')
// Tile 3: Bundle slider
const componentCount = ref(12)
const estimatedSavings = computed(() => (componentCount.value * 42).toFixed(0))
// Tile 4: Query execution simulator
const isRunningQuery = ref(false)
const queryLatency = ref('12ms')
function triggerQuery() {
isRunningQuery.value = true
setTimeout(() => {
queryLatency.value = `${Math.floor(Math.random() * 8) + 8}ms`
isRunningQuery.value = false
}, 400)
}
</script>
<template>
<section
data-slot="hero-bento-preview-grid"
class="bg-background relative overflow-hidden px-4 py-16 sm:px-6 sm:py-24 lg:px-8"
>
<!-- Center Ambient Glow -->
<div
class="bg-primary/10 pointer-events-none absolute top-1/4 left-1/2 -z-10 h-96 w-full max-w-5xl -translate-x-1/2 -translate-y-1/2 rounded-full blur-xl"
/>
<div class="mx-auto max-w-7xl space-y-12 text-center">
<!-- Headline & Subtitle -->
<div class="mx-auto max-w-3xl space-y-5">
<Badge variant="secondary" class="gap-1.5 px-3 py-1 font-mono text-xs shadow-xs">
<Layers class="text-primary size-3.5" />
The Unbundled Design Engineering Platform
</Badge>
<h1 class="text-foreground text-4xl leading-[1.12] font-bold tracking-tight sm:text-5xl lg:text-6xl">
Everything design engineers need to build at lightspeed.
</h1>
<p class="text-muted-foreground text-base leading-relaxed sm:text-lg">
Tactile workbenches, OKLCH fluid color palettes, and guaranteed cross-framework parity—composed right in your
hero.
</p>
<!-- CTA Buttons -->
<div class="flex flex-wrap items-center justify-center gap-3 pt-2">
<Button as="a" href="#explore" size="lg" class="gap-2 font-semibold shadow-xs">
<span>Explore 300+ Workbenches</span>
<ArrowRight class="size-4" />
</Button>
<Button as="a" href="#docs" variant="outline" size="lg" class="gap-2 font-medium">
<Code2 class="text-muted-foreground size-4" />
<span>View Architecture</span>
</Button>
</div>
</div>
<!-- 3-Column Asymmetric Bento Hero Grid -->
<div class="mx-auto grid max-w-6xl grid-cols-1 gap-4 pt-4 text-left sm:gap-6 md:grid-cols-3">
<!-- Tile 1: OKLCH Theme Customizer (2 Cols) -->
<Card
class="border-border bg-card/95 flex flex-col justify-between space-y-6 rounded-2xl p-6 shadow-lg backdrop-blur-md md:col-span-2"
>
<div class="space-y-2">
<div class="flex items-center justify-between">
<span class="text-primary inline-flex items-center gap-1.5 font-mono text-xs font-semibold">
<Palette class="size-3.5" /> OKLCH Dynamic Theme Engine
</span>
<Badge variant="outline" class="font-mono text-xs">Zero Runtime CSS</Badge>
</div>
<h3 class="text-foreground text-lg font-bold">Interactive Palette & Contrast Synthesizer</h3>
<p class="text-muted-foreground text-xs">
Select a brand accent and watch UI components dynamically adjust perceptual contrast.
</p>
</div>
<!-- Color Buttons & Live Preview -->
<div class="border-border bg-background/60 space-y-4 rounded-xl border p-4">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<button
v-for="(c, idx) in colorThemes"
:key="c.name"
type="button"
class="size-6 rounded-full border-2 transition-transform"
:class="
activeColorIndex === idx
? 'border-foreground scale-125 shadow-xs'
: 'border-transparent opacity-70 hover:opacity-100'
"
:style="{ backgroundColor: c.hex }"
@click="activeColorIndex = idx"
/>
</div>
<div class="text-muted-foreground flex items-center gap-1 font-mono text-xs">
<span>Mode:</span>
<button
type="button"
class="border-border hover:bg-muted rounded border p-1 transition-colors"
@click="isDarkPreview = !isDarkPreview"
>
<Sun v-if="!isDarkPreview" class="text-warning size-3.5" />
<Moon v-else class="text-muted-foreground size-3.5" />
</button>
</div>
</div>
<!-- Live Button Demo with Active Hue -->
<div class="flex flex-wrap items-center gap-3 pt-2">
<button
type="button"
class="rounded-lg px-4 py-2 text-xs font-semibold text-white shadow-xs transition-transform active:scale-95"
:style="{ backgroundColor: colorThemes[activeColorIndex].hex }"
>
Synthesized Action Button
</button>
<button
type="button"
class="border-border bg-background text-foreground hover:bg-muted rounded-lg border px-4 py-2 text-xs font-medium transition-colors"
>
Secondary Token
</button>
</div>
</div>
</Card>
<!-- Tile 2: Dual Framework Parity (1 Col) -->
<Card
class="border-border bg-card/95 flex flex-col justify-between space-y-4 rounded-2xl p-6 shadow-lg backdrop-blur-md"
>
<div class="space-y-2">
<div class="flex items-center justify-between">
<span class="text-success inline-flex items-center gap-1.5 font-mono text-xs font-semibold">
<CheckCircle2 class="size-3.5" /> 100% Mirror Parity
</span>
</div>
<h3 class="text-foreground text-lg font-bold">Vue 3.5 & React 19</h3>
<p class="text-muted-foreground text-xs">
Identical CVA variant matrix and AST output across both frameworks.
</p>
</div>
<!-- Framework Toggle -->
<div class="space-y-3">
<div class="bg-muted/40 border-border grid grid-cols-2 gap-1 rounded-lg border p-1">
<button
type="button"
class="rounded-md py-1.5 font-mono text-xs font-semibold transition-colors"
:class="activeFramework === 'vue' ? 'bg-background text-success shadow-xs' : 'text-muted-foreground'"
@click="activeFramework = 'vue'"
>
Vue 3.5 SFC
</button>
<button
type="button"
class="rounded-md py-1.5 font-mono text-xs font-semibold transition-colors"
:class="activeFramework === 'react' ? 'bg-background text-info shadow-xs' : 'text-muted-foreground'"
@click="activeFramework = 'react'"
>
React 19 TSX
</button>
</div>
<!-- Code snippet snippet -->
<div class="bg-muted/30 border-border/80 text-muted-foreground rounded-lg border p-3 font-mono text-xs">
<span class="text-primary font-bold">></span>
<span v-if="activeFramework === 'vue'"> <Button variant="primary"></span>
<span v-else> <Button variant="primary"></span>
<div class="text-success mt-0.5 font-medium">✓ CVA Type Contract Validated</div>
</div>
</div>
</Card>
<!-- Tile 3: Zero Bundle Bloat Calculator (1 Col) -->
<Card
class="border-border bg-card/95 flex flex-col justify-between space-y-4 rounded-2xl p-6 shadow-lg backdrop-blur-md"
>
<div class="space-y-2">
<span class="text-warning inline-flex items-center gap-1.5 font-mono text-xs font-semibold">
<Cpu class="size-3.5" /> Zero NPM Overhead
</span>
<h3 class="text-foreground text-lg font-bold">Own Your Source Code</h3>
<p class="text-muted-foreground text-xs">
Slide to calculate bundle weight saved compared to monolithic packages.
</p>
</div>
<!-- Slider Widget -->
<div class="border-border bg-background/60 space-y-3 rounded-xl border p-4">
<div class="flex items-center justify-between font-mono text-xs">
<span class="text-muted-foreground">Installed Blocks:</span>
<span class="text-foreground font-bold">{{ componentCount }}</span>
</div>
<input
v-model.number="componentCount"
type="range"
min="1"
max="50"
class="accent-primary w-full cursor-pointer"
/>
<div class="border-border/60 flex items-center justify-between border-t pt-1 font-mono text-xs">
<span class="text-muted-foreground">Bundle Saved:</span>
<span class="text-success font-mono font-bold">~{{ estimatedSavings }} kB</span>
</div>
</div>
</Card>
<!-- Tile 4: Sub-millisecond Execution Speed (2 Cols) -->
<Card
class="border-border bg-card/95 flex flex-col justify-between space-y-4 rounded-2xl p-6 shadow-lg backdrop-blur-md md:col-span-2"
>
<div class="flex items-center justify-between">
<div class="space-y-1">
<span class="text-primary inline-flex items-center gap-1.5 font-mono text-xs font-semibold">
<Zap class="size-3.5" /> Instant Hydration & Zero Latency
</span>
<h3 class="text-foreground text-lg font-bold">Edge Optimized Architecture</h3>
</div>
<Button size="sm" variant="outline" class="gap-1.5 font-mono text-xs" @click="triggerQuery">
<Play v-if="!isRunningQuery" class="size-3 fill-current" />
<Activity v-else class="size-3 animate-spin" />
<span>Ping Edge</span>
</Button>
</div>
<!-- Latency Telemetry Row -->
<div class="grid grid-cols-3 gap-3">
<div class="border-border bg-background/50 space-y-0.5 rounded-xl border p-3">
<p class="text-muted-foreground font-mono text-xs">Round-Trip Latency</p>
<p class="text-success font-mono text-xl font-bold">{{ queryLatency }}</p>
</div>
<div class="border-border bg-background/50 space-y-0.5 rounded-xl border p-3">
<p class="text-muted-foreground font-mono text-xs">DOM Mutation Time</p>
<p class="text-foreground font-mono text-xl font-bold">< 1.2ms</p>
</div>
<div class="border-border bg-background/50 space-y-0.5 rounded-xl border p-3">
<p class="text-muted-foreground font-mono text-xs">Accessibility Score</p>
<p class="text-primary font-mono text-xl font-bold">100 / 100</p>
</div>
</div>
</Card>
</div>
</div>
</section>
</template>
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { ArrowRight, Check, Code2, Copy, RotateCcw } from 'lucide-vue-next'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
interface CommandPreset {
id: string
label: string
command: string
output: string[]
}
const presets: CommandPreset[] = [
{
id: 'init',
label: '1. Init Theme',
command: 'npx shadcn-vue@latest add https://uipkge.dev/r/vue/init.json',
output: [
'✔ Resolving registry dependencies...',
'✔ Downloaded packages/shared/styles/tailwind.css (OKLCH @theme inline)',
'✔ Configured cn() utility in src/lib/utils.ts',
'✔ Injected useTheme composable with system preference sync',
'✔ UIPKGE bootstrap complete in 124ms.',
],
},
{
id: 'component',
label: '2. Add KPI Grid',
command: 'npx shadcn-vue@latest add https://uipkge.dev/r/vue/kpi-grid.json',
output: [
'✔ Fetched manifest for kpi-grid (registry:ui)',
'✔ Added components/ui/kpi-grid/KpiGrid.vue',
'✔ Verified CVA variants in kpi-grid.variants.ts',
'✔ Installed transitive primitives: Card, Badge, Sparkline',
'✔ Zero npm bundle overhead added to node_modules.',
],
},
{
id: 'verify',
label: '3. Parity Check',
command: 'npm run check:parity',
output: [
'✔ Checking 31 shared CVA variant definitions...',
'✔ Validating Vue 3.5 SFC and React 19 TSX type contracts...',
'✔ Token synchronization: styles/tailwind.css (100% match)',
'✔ PASS: Full cross-framework parity confirmed across all 300+ items.',
],
},
]
const activePresetIndex = ref(0)
const activeTab = ref<'terminal' | 'source'>('terminal')
const displayedLines = ref<string[]>([])
const isExecuting = ref(false)
const copied = ref(false)
const executionSpeed = ref<1 | 2>(1)
let executionTimeout: ReturnType<typeof setTimeout> | undefined
function runPreset(index: number) {
activePresetIndex.value = index
activeTab.value = 'terminal'
displayedLines.value = []
isExecuting.value = true
if (executionTimeout) clearTimeout(executionTimeout)
const currentPreset = presets[index]
const lines = currentPreset.output
const delay = executionSpeed.value === 1 ? 160 : 70
lines.forEach((line, i) => {
executionTimeout = setTimeout(
() => {
displayedLines.value.push(line)
if (i === lines.length - 1) {
isExecuting.value = false
}
},
(i + 1) * delay,
)
})
}
function copyCommand() {
const current = presets[activePresetIndex.value]
navigator.clipboard.writeText(current.command)
copied.value = true
setTimeout(() => (copied.value = false), 2000)
}
onMounted(() => {
runPreset(0)
})
onUnmounted(() => {
if (executionTimeout) clearTimeout(executionTimeout)
})
const currentPreset = computed(() => presets[activePresetIndex.value])
</script>
<template>
<section
data-slot="hero-developer-terminal"
class="bg-background relative overflow-hidden px-4 py-16 sm:px-6 sm:py-24 lg:px-8"
>
<div class="mx-auto grid max-w-7xl grid-cols-1 items-center gap-12 lg:grid-cols-12 lg:gap-8">
<!-- Left Column: Value Proposition & CTAs (6 Cols) -->
<div class="space-y-6 text-left lg:col-span-6">
<!-- Parity Badge -->
<div
class="border-border bg-muted/40 inline-flex items-center gap-2 rounded-full border px-3 py-1 font-mono text-xs shadow-xs"
>
<span class="bg-success size-2 rounded-full" />
<span class="text-foreground font-medium">Dual-Framework UI Registry</span>
<span class="text-muted-foreground">•</span>
<span class="text-muted-foreground">Vue 3.5 & React 19</span>
</div>
<!-- Main Headline -->
<h1 class="text-foreground text-4xl leading-[1.1] font-bold tracking-tight sm:text-5xl lg:text-6xl">
The unbundled UI registry for design engineers.
</h1>
<!-- Subtitle -->
<p class="text-muted-foreground max-w-xl text-base leading-relaxed sm:text-lg">
Own your source code. Copy production-grade components and dense workbenches directly into your project with
zero npm runtime lock-in.
</p>
<!-- CTA Buttons -->
<div class="flex flex-wrap items-center gap-3 pt-2">
<Button as="a" href="#components" size="lg" class="gap-2 font-semibold shadow-xs">
<span>Explore 300+ Blocks</span>
<ArrowRight class="size-4" />
</Button>
<Button as="a" href="#spec" variant="outline" size="lg" class="gap-2 font-medium">
<Code2 class="text-muted-foreground size-4" />
<span>Registry Spec</span>
</Button>
</div>
<!-- Telemetry Metrics Strip -->
<div class="border-border/80 grid grid-cols-3 gap-4 border-t pt-6 text-left">
<div>
<p class="text-foreground font-mono text-2xl font-bold">300+</p>
<p class="text-muted-foreground mt-0.5 text-xs">Workbenches & Primitives</p>
</div>
<div>
<p class="text-foreground font-mono text-2xl font-bold">0 kB</p>
<p class="text-muted-foreground mt-0.5 text-xs">Runtime Package Bloat</p>
</div>
<div>
<p class="text-foreground font-mono text-2xl font-bold">100%</p>
<p class="text-muted-foreground mt-0.5 text-xs">Cross-Framework Parity</p>
</div>
</div>
</div>
<!-- Right Column: Interactive Developer Terminal Workbench (6 Cols) -->
<div class="lg:col-span-6">
<Card class="border-border bg-card overflow-hidden rounded-xl shadow-sm">
<!-- Terminal Header -->
<div class="border-border bg-muted/40 flex items-center justify-between border-b px-4 py-2.5">
<!-- Window Controls & File Path -->
<div class="flex items-center gap-2">
<div class="flex gap-1.5">
<div class="bg-destructive/80 size-3 rounded-full" />
<div class="bg-warning/80 size-3 rounded-full" />
<div class="bg-success/80 size-3 rounded-full" />
</div>
<span class="text-muted-foreground ml-2 font-mono text-xs">uipkge-cli — bash</span>
</div>
<!-- Action Controls -->
<div class="flex items-center gap-1.5">
<!-- Speed Selector -->
<button
type="button"
class="border-border bg-background text-muted-foreground hover:text-foreground rounded border px-2 py-0.5 font-mono text-xs transition-colors"
@click="executionSpeed = executionSpeed === 1 ? 2 : 1"
>
{{ executionSpeed }}x speed
</button>
<!-- Copy Button -->
<button
type="button"
class="border-border bg-background text-muted-foreground hover:text-foreground flex items-center gap-1 rounded border px-2 py-0.5 font-mono text-xs transition-colors"
@click="copyCommand"
>
<Check v-if="copied" class="text-success size-3" />
<Copy v-else class="size-3" />
<span>{{ copied ? 'Copied' : 'Copy' }}</span>
</button>
</div>
</div>
<!-- Preset Navigation Tabs -->
<div class="border-border/80 bg-muted/20 flex items-center overflow-x-auto border-b px-2">
<button
v-for="(preset, idx) in presets"
:key="preset.id"
type="button"
class="border-b-2 px-3 py-2 font-mono text-xs whitespace-nowrap transition-colors"
:class="
activePresetIndex === idx
? 'border-primary text-foreground bg-background/50 font-semibold'
: 'text-muted-foreground hover:text-foreground border-transparent'
"
@click="runPreset(idx)"
>
{{ preset.label }}
</button>
</div>
<!-- Terminal Content Viewport -->
<div class="bg-card min-h-[220px] space-y-3 p-4 font-mono text-xs sm:p-5">
<!-- Command Input Line -->
<div class="text-foreground flex items-start gap-2">
<span class="text-primary font-bold select-none">></span>
<span class="text-foreground font-medium break-all">{{ currentPreset.command }}</span>
</div>
<!-- Output Lines -->
<div class="space-y-1.5 pt-1">
<div
v-for="(line, idx) in displayedLines"
:key="idx"
class="text-muted-foreground flex items-center gap-2 transition-colors duration-150"
>
<span class="text-success shrink-0 font-bold select-none">✓</span>
<span class="text-xs">{{ line.replace(/^✔\s*/, '') }}</span>
</div>
<!-- Typing / Loading Cursor -->
<div v-if="isExecuting" class="text-muted-foreground flex items-center gap-2 pt-1">
<span class="bg-primary size-1.5 rounded-full" />
<span class="text-muted-foreground text-xs italic">Streaming AST payload...</span>
</div>
</div>
</div>
<!-- Terminal Footer Status Bar -->
<div
class="border-border bg-muted/30 text-muted-foreground flex items-center justify-between border-t px-4 py-2 font-mono text-xs"
>
<div class="flex items-center gap-2">
<span class="bg-success size-2 rounded-full" />
<span>Registry v2.4.0 (OKLCH)</span>
</div>
<button
type="button"
class="text-primary inline-flex items-center gap-1 hover:underline"
@click="runPreset(activePresetIndex)"
>
<RotateCcw class="size-3" />
<span>Re-run</span>
</button>
</div>
</Card>
</div>
</div>
</section>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { Activity, ArrowRight, Check, Copy, Globe, Radio, Server, ShieldCheck, Terminal, Zap } from 'lucide-vue-next'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
import { cn } from '@/lib/utils'
export interface EdgeNode {
id: string
name: string
region: string
country: string
city: string
latencyMs: number
p99LatencyMs: number
cacheHitRatio: number
qps: number
status: 'optimal' | 'operational' | 'degraded'
coordinates: { x: number; y: number }
}
export interface HeroGlobeInteractiveNodesProps {
title?: string
description?: string
cliCommand?: string
nodes?: EdgeNode[]
class?: string
}
const DEFAULT_NODES: EdgeNode[] = [
{
id: 'node-iad',
name: 'iad-1',
region: 'us-east-1',
country: 'United States',
city: 'Washington, D.C.',
latencyMs: 8,
p99LatencyMs: 14,
cacheHitRatio: 99.4,
qps: 18450,
status: 'optimal',
coordinates: { x: 28, y: 38 },
},
{
id: 'node-sfo',
name: 'sfo-1',
region: 'us-west-1',
country: 'United States',
city: 'San Francisco',
latencyMs: 12,
p99LatencyMs: 22,
cacheHitRatio: 99.1,
qps: 14200,
status: 'optimal',
coordinates: { x: 18, y: 40 },
},
{
id: 'node-fra',
name: 'fra-1',
region: 'eu-central-1',
country: 'Germany',
city: 'Frankfurt',
latencyMs: 15,
p99LatencyMs: 27,
cacheHitRatio: 98.9,
qps: 21300,
status: 'optimal',
coordinates: { x: 52, y: 32 },
},
{
id: 'node-sin',
name: 'sin-1',
region: 'ap-southeast-1',
country: 'Singapore',
city: 'Singapore',
latencyMs: 24,
p99LatencyMs: 41,
cacheHitRatio: 98.6,
qps: 16800,
status: 'optimal',
coordinates: { x: 76, y: 58 },
},
{
id: 'node-nrt',
name: 'nrt-1',
region: 'ap-northeast-1',
country: 'Japan',
city: 'Tokyo',
latencyMs: 19,
p99LatencyMs: 34,
cacheHitRatio: 99.2,
qps: 19400,
status: 'optimal',
coordinates: { x: 84, y: 42 },
},
{
id: 'node-syd',
name: 'syd-1',
region: 'ap-southeast-2',
country: 'Australia',
city: 'Sydney',
latencyMs: 32,
p99LatencyMs: 52,
cacheHitRatio: 98.2,
qps: 9800,
status: 'optimal',
coordinates: { x: 88, y: 78 },
},
{
id: 'node-gru',
name: 'gru-1',
region: 'sa-east-1',
country: 'Brazil',
city: 'São Paulo',
latencyMs: 44,
p99LatencyMs: 68,
cacheHitRatio: 97.9,
qps: 7600,
status: 'optimal',
coordinates: { x: 36, y: 74 },
},
{
id: 'node-lhr',
name: 'lhr-1',
region: 'eu-west-2',
country: 'United Kingdom',
city: 'London',
latencyMs: 11,
p99LatencyMs: 20,
cacheHitRatio: 99.5,
qps: 24600,
status: 'optimal',
coordinates: { x: 48, y: 30 },
},
]
const props = withDefaults(defineProps<HeroGlobeInteractiveNodesProps>(), {
title: 'Sub-millisecond UI edge delivery across 310+ PoPs globally.',
description:
'Compile unbundled Vue 3.5 and React 19 components with instant edge caching. Zero cold starts, deterministic tree-shaking, and hardware-accelerated telemetry.',
cliCommand: 'npx shadcn-vue add @uipkge/init',
})
const activeNodes = computed(() => props.nodes ?? DEFAULT_NODES)
const selectedNodeId = ref<string>('node-iad')
const copied = ref(false)
const selectedNode = computed<EdgeNode>(() => {
return activeNodes.value.find((n) => n.id === selectedNodeId.value) || activeNodes.value[0]
})
const avgGlobalLatency = computed(() => {
if (!activeNodes.value.length) return 0
const sum = activeNodes.value.reduce((acc, curr) => acc + curr.latencyMs, 0)
return Math.round(sum / activeNodes.value.length)
})
const totalGlobalQps = computed(() => {
return activeNodes.value.reduce((acc, curr) => acc + curr.qps, 0).toLocaleString()
})
async function copyCommand() {
try {
await navigator.clipboard.writeText(props.cliCommand)
copied.value = true
setTimeout(() => {
copied.value = false
}, 2000)
} catch {
// clipboard fallback
}
}
</script>
<template>
<section
data-slot="hero-globe-interactive-nodes"
:class="cn('bg-background relative overflow-hidden py-16 sm:py-24 lg:py-28', props.class)"
>
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 gap-12 lg:grid-cols-12 lg:items-center">
<!-- Left: Copy & Actions (6 Cols) -->
<div class="space-y-6 lg:col-span-6">
<div
class="border-primary/20 bg-primary/10 text-primary inline-flex items-center gap-2 rounded-full border px-3 py-1 text-xs font-medium shadow-2xs"
>
<Radio class="text-primary size-3.5 animate-pulse" />
<span>Global Edge CDN Mesh Active • 310+ PoPs</span>
</div>
<h1 class="text-foreground text-3xl font-bold tracking-tight sm:text-4xl lg:text-5xl">
{{ title }}
</h1>
<p class="text-muted-foreground text-base sm:text-lg">
{{ description }}
</p>
<!-- CLI Command Sandbox -->
<div class="flex flex-col gap-3 sm:flex-row sm:items-center">
<div
class="border-border bg-muted/60 text-foreground flex items-center justify-between rounded-lg border px-3 py-2 font-mono text-xs shadow-2xs sm:w-80"
>
<div class="flex items-center gap-2 truncate">
<Terminal class="text-muted-foreground size-3.5 shrink-0" />
<span class="truncate">{{ cliCommand }}</span>
</div>
<Button
variant="ghost"
size="icon"
class="text-muted-foreground hover:text-foreground size-7 shrink-0"
@click="copyCommand"
>
<Check v-if="copied" class="text-primary size-3.5" />
<Copy v-else class="size-3.5" />
</Button>
</div>
<Button size="default" class="gap-1.5 shadow-xs">
<span>Explore Registry</span>
<ArrowRight class="size-4" />
</Button>
</div>
<!-- Quick Telemetry Strip -->
<div class="border-border grid grid-cols-3 gap-4 border-t pt-6">
<div class="space-y-1">
<div class="text-muted-foreground text-xs font-medium">Global Avg Ping</div>
<div class="text-foreground text-xl font-bold tracking-tight">{{ avgGlobalLatency }}ms</div>
<div class="text-success text-xs font-medium">−42% vs origin</div>
</div>
<div class="space-y-1">
<div class="text-muted-foreground text-xs font-medium">Network Throughput</div>
<div class="text-foreground text-xl font-bold tracking-tight">{{ totalGlobalQps }}</div>
<div class="text-muted-foreground text-xs">req/sec live</div>
</div>
<div class="space-y-1">
<div class="text-muted-foreground text-xs font-medium">Edge Availability</div>
<div class="text-foreground text-xl font-bold tracking-tight">99.95%</div>
<div class="text-success text-xs font-medium">Enterprise SLA</div>
</div>
</div>
</div>
<!-- Right: Interactive SVG Node Map & Telemetry Workbench (6 Cols) -->
<div class="space-y-4 lg:col-span-6">
<Card class="border-border bg-card/80 shadow-sm backdrop-blur-xs">
<CardContent class="space-y-4 p-4 sm:p-6">
<!-- Top bar: active node switch -->
<div class="border-border flex items-center justify-between border-b pb-3">
<div class="flex items-center gap-2">
<Globe class="text-primary size-4" />
<span class="text-foreground text-xs font-semibold">Active Edge Inspection</span>
</div>
<Badge variant="outline" class="border-border text-muted-foreground font-mono text-xs">
{{ selectedNode.city }} ({{ selectedNode.name }})
</Badge>
</div>
<!-- Interactive Node Grid / Map Canvas -->
<div class="border-border/80 bg-muted/30 relative h-60 w-full overflow-hidden rounded-lg border p-3">
<!-- SVG Connections Network Layer -->
<svg class="stroke-border/70 absolute inset-0 size-full" xmlns="http://www.w3.org/2000/svg">
<line
v-for="(n, idx) in activeNodes.slice(1)"
:key="idx"
:x1="`${activeNodes[0].coordinates.x}%`"
:y1="`${activeNodes[0].coordinates.y}%`"
:x2="`${n.coordinates.x}%`"
:y2="`${n.coordinates.y}%`"
stroke-dasharray="3 3"
stroke-width="1"
/>
</svg>
<!-- Interactive Region Nodes -->
<button
v-for="node in activeNodes"
:key="node.id"
type="button"
:style="{ left: `${node.coordinates.x}%`, top: `${node.coordinates.y}%` }"
:class="
cn(
'focus-visible:ring-ring absolute flex -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full transition-colors duration-150 focus-visible:ring-2 focus-visible:outline-none',
selectedNodeId === node.id
? 'bg-primary text-primary-foreground ring-primary/20 z-10 size-7 scale-110 shadow-sm ring-4'
: 'bg-card hover:bg-accent border-border text-foreground size-5 border hover:scale-105',
)
"
@click="selectedNodeId = node.id"
>
<span class="font-mono text-xs leading-none font-bold">
{{ selectedNodeId === node.id ? node.name.slice(0, 3).toUpperCase() : '' }}
</span>
<span v-if="selectedNodeId !== node.id" class="bg-success size-2 rounded-full" />
</button>
</div>
<!-- Edge Node Telemetry Card -->
<div class="border-border bg-background grid grid-cols-2 gap-2.5 rounded-lg border p-3 sm:grid-cols-4">
<div class="space-y-0.5">
<div class="text-muted-foreground flex items-center gap-1 text-xs">
<Zap class="text-primary size-3" />
<span>Ping (p50)</span>
</div>
<div class="text-foreground font-mono text-sm font-bold">{{ selectedNode.latencyMs }}ms</div>
</div>
<div class="space-y-0.5">
<div class="text-muted-foreground flex items-center gap-1 text-xs">
<Activity class="text-success size-3" />
<span>Ping (p99)</span>
</div>
<div class="text-foreground font-mono text-sm font-bold">{{ selectedNode.p99LatencyMs }}ms</div>
</div>
<div class="space-y-0.5">
<div class="text-muted-foreground flex items-center gap-1 text-xs">
<Server class="text-info size-3" />
<span>Cache Hit</span>
</div>
<div class="text-foreground font-mono text-sm font-bold">{{ selectedNode.cacheHitRatio }}%</div>
</div>
<div class="space-y-0.5">
<div class="text-muted-foreground flex items-center gap-1 text-xs">
<ShieldCheck class="text-primary size-3" />
<span>Throughput</span>
</div>
<div class="text-foreground font-mono text-sm font-bold">
{{ selectedNode.qps.toLocaleString() }} qps
</div>
</div>
</div>
<!-- Quick Node Pill Selector -->
<div class="flex flex-wrap gap-1.5 pt-1">
<button
v-for="node in activeNodes"
:key="node.id"
type="button"
:class="
cn(
'rounded-md border px-2 py-1 font-mono text-xs font-medium transition-colors',
selectedNodeId === node.id
? 'border-primary bg-primary/10 text-primary'
: 'border-border bg-card text-muted-foreground hover:text-foreground hover:bg-accent/50',
)
"
@click="selectedNodeId = node.id"
>
{{ node.name }} • {{ node.city }}
</button>
</div>
</CardContent>
</Card>
</div>
</div>
</div>
</section>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { ArrowRight, CheckCircle2, Play, ShieldCheck, TrendingUp } from 'lucide-vue-next'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
type Timeframe = 'live' | '24h' | '7d' | '30d'
interface MetricData {
mrr: string
growth: string
activeUsers: string
requests: string
chartPoints: number[]
}
const metricsByTimeframe: Record<Timeframe, MetricData> = {
live: {
mrr: '$148,920',
growth: '+24.6%',
activeUsers: '14,280',
requests: '1.42M/min',
chartPoints: [28, 42, 36, 54, 48, 62, 78, 70, 85, 92, 88, 98],
},
'24h': {
mrr: '$146,800',
growth: '+18.2%',
activeUsers: '42,100',
requests: '84.6M',
chartPoints: [35, 38, 45, 52, 50, 68, 64, 75, 82, 80, 89, 94],
},
'7d': {
mrr: '$139,400',
growth: '+31.4%',
activeUsers: '185,400',
requests: '592M',
chartPoints: [20, 28, 35, 42, 55, 60, 72, 68, 80, 85, 90, 100],
},
'30d': {
mrr: '$124,100',
growth: '+44.1%',
activeUsers: '490,000',
requests: '2.4B',
chartPoints: [15, 22, 30, 38, 48, 58, 65, 74, 82, 88, 94, 105],
},
}
const activeTimeframe = ref<Timeframe>('live')
const activeMetric = computed(() => metricsByTimeframe[activeTimeframe.value])
const chartSvgPath = computed(() => {
const points = activeMetric.value.chartPoints
const width = 500
const height = 120
const max = Math.max(...points)
const min = Math.min(...points)
const range = max - min || 1
return points
.map((val, idx) => {
const x = (idx / (points.length - 1)) * width
const y = height - ((val - min) / range) * (height - 20) - 10
return `${idx === 0 ? 'M' : 'L'} ${x.toFixed(1)} ${y.toFixed(1)}`
})
.join(' ')
})
const chartAreaPath = computed(() => {
return `${chartSvgPath.value} L 500 120 L 0 120 Z`
})
</script>
<template>
<section
data-slot="hero-saas-metrics-dashboard"
class="bg-background relative overflow-hidden px-4 py-16 sm:px-6 sm:py-24 lg:px-8"
>
<!-- Radial Glow Background -->
<div
class="bg-primary/10 pointer-events-none absolute top-0 left-1/2 -z-10 h-80 w-full max-w-6xl -translate-x-1/2 rounded-full blur-xl"
/>
<div class="mx-auto max-w-6xl space-y-12 text-center">
<!-- Header Copy -->
<div class="mx-auto max-w-3xl space-y-5">
<Badge variant="secondary" class="gap-1.5 px-3 py-1 font-mono text-xs shadow-xs">
<TrendingUp class="text-primary size-3.5" />
Enterprise Revenue & Event Intelligence
</Badge>
<h1 class="text-foreground text-4xl leading-[1.12] font-bold tracking-tight sm:text-5xl lg:text-6xl">
Real-time telemetry for modern engineering teams.
</h1>
<p class="text-muted-foreground text-base leading-relaxed sm:text-lg">
Monitor revenue acceleration, API transaction throughput, and compute workloads with sub-millisecond
precision.
</p>
<!-- CTA Action Buttons -->
<div class="flex flex-wrap items-center justify-center gap-3 pt-2">
<Button as="a" href="#start" size="lg" class="gap-2 font-semibold shadow-xs">
<span>Start Free Sandbox</span>
<ArrowRight class="size-4" />
</Button>
<Button as="a" href="#demo" variant="outline" size="lg" class="gap-2 font-medium">
<Play class="size-3.5 fill-current" />
<span>Interactive Demo</span>
</Button>
</div>
<div class="text-muted-foreground flex items-center justify-center gap-6 pt-2 font-mono text-xs">
<span class="inline-flex items-center gap-1.5">
<ShieldCheck class="text-success size-4" /> SOC2 Type II Certified
</span>
<span class="inline-flex items-center gap-1.5">
<CheckCircle2 class="text-primary size-4" /> 99.95% SLA Uptime
</span>
</div>
</div>
<!-- Interactive SaaS Dashboard Preview Card -->
<div class="relative mx-auto max-w-5xl">
<Card class="border-border bg-card/95 overflow-hidden rounded-2xl text-left shadow-sm backdrop-blur-md">
<!-- Mock Window Top Bar -->
<div class="border-border bg-muted/40 flex items-center justify-between border-b px-4 py-3">
<div class="flex items-center gap-3">
<div class="flex gap-1.5">
<div class="bg-destructive/80 size-3 rounded-full" />
<div class="bg-warning/80 size-3 rounded-full" />
<div class="bg-success/80 size-3 rounded-full" />
</div>
<span class="text-foreground font-mono text-xs font-medium">console.uipkge.dev/production</span>
</div>
<!-- Timeframe Filter Tabs -->
<div class="bg-background border-border flex items-center gap-1 rounded-lg border p-0.5">
<button
v-for="tf in ['live', '24h', '7d', '30d'] as Timeframe[]"
:key="tf"
type="button"
class="rounded-md px-2.5 py-1 font-mono text-xs uppercase transition-colors"
:class="
activeTimeframe === tf
? 'bg-primary text-primary-foreground font-semibold shadow-xs'
: 'text-muted-foreground hover:text-foreground'
"
@click="activeTimeframe = tf"
>
{{ tf }}
</button>
</div>
</div>
<!-- Dashboard Metric Cards Grid -->
<div class="space-y-6 p-6 sm:p-8">
<div class="grid grid-cols-1 gap-4 sm:grid-cols-3">
<!-- MRR Card -->
<div class="border-border bg-background/50 space-y-1 rounded-xl border p-4">
<div class="text-muted-foreground flex items-center justify-between text-xs">
<span class="font-mono">Recurring Revenue</span>
<Badge variant="outline" class="border-success/20 bg-success/10 text-success font-mono text-xs">
{{ activeMetric.growth }}
</Badge>
</div>
<p class="text-foreground font-mono text-2xl font-bold">{{ activeMetric.mrr }}</p>
<p class="text-muted-foreground text-xs">Calculated across active subscriptions</p>
</div>
<!-- Active Workloads Card -->
<div class="border-border bg-background/50 space-y-1 rounded-xl border p-4">
<div class="text-muted-foreground flex items-center justify-between text-xs">
<span class="font-mono">Active Nodes & Pods</span>
<span class="bg-success size-2 rounded-full" />
</div>
<p class="text-foreground font-mono text-2xl font-bold">{{ activeMetric.activeUsers }}</p>
<p class="text-muted-foreground text-xs">Distributed across 8 global regions</p>
</div>
<!-- Request Throughput Card -->
<div class="border-border bg-background/50 space-y-1 rounded-xl border p-4">
<div class="text-muted-foreground flex items-center justify-between text-xs">
<span class="font-mono">Throughput</span>
<TrendingUp class="text-primary size-3.5" />
</div>
<p class="text-foreground font-mono text-2xl font-bold">{{ activeMetric.requests }}</p>
<p class="text-muted-foreground text-xs">Edge cached queries & websocket streams</p>
</div>
</div>
<!-- Interactive Area Trend Chart -->
<div class="border-border bg-background/30 space-y-3 rounded-xl border p-5">
<div class="flex items-center justify-between">
<div>
<p class="text-muted-foreground font-mono text-xs uppercase">Transaction Velocity & Event Stream</p>
<p class="text-foreground text-sm font-semibold">Global Ingestion Rate</p>
</div>
<span class="text-success font-mono text-xs font-medium">99.998% Success</span>
</div>
<!-- SVG Area Chart -->
<div class="relative h-28 w-full overflow-hidden">
<svg class="h-full w-full" viewBox="0 0 500 120" preserveAspectRatio="none">
<defs>
<linearGradient id="chartGlow" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="var(--primary)" stop-opacity="0.35" />
<stop offset="100%" stop-color="var(--primary)" stop-opacity="0.0" />
</linearGradient>
</defs>
<!-- Gradient Area -->
<path :d="chartAreaPath" fill="url(#chartGlow)" />
<!-- Stroke Line -->
<path
:d="chartSvgPath"
fill="none"
stroke="var(--primary)"
stroke-width="2.5"
stroke-linecap="round"
/>
</svg>
</div>
</div>
</div>
</Card>
</div>
</div>
</section>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { ArrowRight, Monitor, Moon, Smartphone, Sun, Tablet } from 'lucide-vue-next'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
import { cn } from '@/lib/utils'
export interface HeroSplitDeviceMockupProps {
title?: string
description?: string
class?: string
}
const props = withDefaults(defineProps<HeroSplitDeviceMockupProps>(), {
title: 'Responsive components with identical fidelity on every device.',
description:
'Every primitive and block is engineered with fluid container queries and zero layout shift across desktop, tablet, and mobile displays.',
})
type DeviceMode = 'desktop' | 'tablet' | 'mobile'
const activeMode = ref<DeviceMode>('desktop')
const isDarkMode = ref(true)
const activeTab = ref<'overview' | 'analytics' | 'activity'>('overview')
const isMetricStreaming = ref(true)
const mockKpis = [
{ label: 'Active Sessions', value: '24,892', change: '+14.2%', status: 'up' },
{ label: 'Conversion Rate', value: '4.82%', change: '+0.6%', status: 'up' },
{ label: 'Avg TTFB', value: '18ms', change: '-4ms', status: 'optimal' },
]
const viewportWidthClass = computed(() => {
if (activeMode.value === 'desktop') return 'w-full max-w-2xl'
if (activeMode.value === 'tablet') return 'w-full max-w-md'
return 'w-full max-w-[320px]'
})
</script>
<template>
<section
data-slot="hero-split-device-mockup"
:class="cn('bg-background relative overflow-hidden py-16 sm:py-24 lg:py-28', props.class)"
>
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<!-- Top Title & Badge Area -->
<div class="mx-auto max-w-3xl space-y-4 text-center">
<a
href="#device-preview"
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"
>
<Smartphone class="text-primary size-3.5" />
<span>Fluid Container Queries • Tailwind v4 Ready</span>
<ArrowRight class="text-muted-foreground size-3 transition-transform group-hover:translate-x-0.5" />
</a>
<h1 class="text-foreground text-3xl font-bold tracking-tight sm:text-4xl lg:text-5xl">
{{ title }}
</h1>
<p class="text-muted-foreground text-base sm:text-lg">
{{ description }}
</p>
<!-- Viewport Controls & Interactive Triggers -->
<div class="flex flex-wrap items-center justify-center gap-2 pt-2">
<div class="border-border bg-muted/50 inline-flex rounded-lg border p-1 shadow-2xs">
<button
type="button"
:class="
cn(
'flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors',
activeMode === 'desktop'
? 'bg-background text-foreground shadow-2xs'
: 'text-muted-foreground hover:text-foreground',
)
"
@click="activeMode = 'desktop'"
>
<Monitor class="size-3.5" />
<span>Desktop (1440px)</span>
</button>
<button
type="button"
:class="
cn(
'flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors',
activeMode === 'tablet'
? 'bg-background text-foreground shadow-2xs'
: 'text-muted-foreground hover:text-foreground',
)
"
@click="activeMode = 'tablet'"
>
<Tablet class="size-3.5" />
<span>Tablet (768px)</span>
</button>
<button
type="button"
:class="
cn(
'flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors',
activeMode === 'mobile'
? 'bg-background text-foreground shadow-2xs'
: 'text-muted-foreground hover:text-foreground',
)
"
@click="activeMode = 'mobile'"
>
<Smartphone class="size-3.5" />
<span>Mobile (375px)</span>
</button>
</div>
<Button variant="outline" size="sm" class="border-border gap-1.5 text-xs" @click="isDarkMode = !isDarkMode">
<Sun v-if="isDarkMode" class="size-3.5" />
<Moon v-else class="size-3.5" />
<span>{{ isDarkMode ? 'Dark Frame' : 'Light Frame' }}</span>
</Button>
</div>
</div>
<!-- Split Device Mockup Canvas -->
<div class="mt-12 flex justify-center">
<div
:class="
cn(
'border-border bg-card rounded-xl border p-2 shadow-md transition-colors duration-300 ease-out sm:p-3',
viewportWidthClass,
isDarkMode ? 'dark' : '',
)
"
>
<!-- Browser / Device Chrome Header -->
<div class="border-border/80 flex items-center justify-between border-b px-2 pt-1 pb-2.5">
<div class="flex items-center gap-1.5">
<span class="bg-destructive/80 size-2.5 rounded-full" />
<span class="bg-warning/80 size-2.5 rounded-full" />
<span class="bg-success/80 size-2.5 rounded-full" />
</div>
<!-- URL Bar -->
<div
class="border-border/60 bg-muted/60 text-muted-foreground mx-auto flex h-6 w-1/2 items-center justify-center truncate rounded-md border px-2 font-mono text-xs"
>
https://uipkge.dev/demo/dashboard-kpis
</div>
<div class="text-muted-foreground flex items-center gap-1">
<span class="bg-success size-2 rounded-full" />
<span class="font-mono text-xs">LIVE</span>
</div>
</div>
<!-- Inside Interactive Device Application -->
<div class="bg-background space-y-4 rounded-lg p-4 pt-4">
<!-- App Header Inside Frame -->
<div class="border-border flex items-center justify-between border-b pb-3">
<div class="flex items-center gap-2">
<div
class="bg-primary text-primary-foreground flex size-7 items-center justify-center rounded-md text-xs font-bold"
>
UI
</div>
<div>
<div class="text-foreground text-xs font-semibold">Platform Metrics</div>
<div class="text-muted-foreground text-xs">Production Cluster (us-east-1)</div>
</div>
</div>
<!-- Inner App Tabs -->
<div class="border-border bg-muted/40 flex gap-1 rounded-md border p-0.5">
<button
type="button"
:class="
cn(
'rounded px-2 py-0.5 text-xs font-medium transition-colors',
activeTab === 'overview'
? 'bg-background text-foreground shadow-2xs'
: 'text-muted-foreground hover:text-foreground',
)
"
@click="activeTab = 'overview'"
>
Overview
</button>
<button
type="button"
:class="
cn(
'rounded px-2 py-0.5 text-xs font-medium transition-colors',
activeTab === 'analytics'
? 'bg-background text-foreground shadow-2xs'
: 'text-muted-foreground hover:text-foreground',
)
"
@click="activeTab = 'analytics'"
>
Analytics
</button>
</div>
</div>
<!-- KPI Metric Grid in Frame -->
<div :class="cn('grid gap-2.5', activeMode === 'mobile' ? 'grid-cols-1' : 'grid-cols-3')">
<Card v-for="(kpi, idx) in mockKpis" :key="idx" class="border-border bg-card/60 shadow-2xs">
<CardContent class="space-y-1 p-3">
<div class="text-muted-foreground text-xs font-medium">{{ kpi.label }}</div>
<div class="text-foreground text-lg font-bold tracking-tight">{{ kpi.value }}</div>
<div class="text-success flex items-center gap-1 text-xs font-medium">
<span>{{ kpi.change }}</span>
<span class="text-muted-foreground">• vs last week</span>
</div>
</CardContent>
</Card>
</div>
<!-- Interactive Wave / Chart Preview -->
<div class="border-border/80 bg-muted/20 space-y-2 rounded-lg border p-3">
<div class="flex items-center justify-between text-xs">
<span class="text-foreground font-medium">Real-time Telemetry Stream</span>
<Badge variant="outline" class="border-border text-success font-mono text-xs"> Synced: 0.2ms </Badge>
</div>
<!-- SVG Mock Sparkline Chart -->
<svg class="stroke-primary fill-primary/10 h-16 w-full" viewBox="0 0 300 60" preserveAspectRatio="none">
<path d="M0 45 Q 25 20, 50 35 T 100 25 T 150 15 T 200 30 T 250 10 T 300 20 L 300 60 L 0 60 Z" />
<path
d="M0 45 Q 25 20, 50 35 T 100 25 T 150 15 T 200 30 T 250 10 T 300 20"
fill="none"
stroke-width="2"
/>
</svg>
</div>
</div>
</div>
</div>
</div>
</section>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { Check, Command, CornerDownLeft, FileCode, FolderGit2, Layers, Palette, Search, Zap } from 'lucide-vue-next'
import { Badge } from '@/components/ui/badge'
import { Card, CardContent } from '@/components/ui/card'
import { cn } from '@/lib/utils'
export interface CommandAction {
id: string
title: string
category: 'Components' | 'Themes' | 'Git' | 'Workflows'
shortcut: string[]
icon: any
description: string
status: string
}
export interface HeroTypingCommandCenterProps {
title?: string
description?: string
class?: string
}
const props = withDefaults(defineProps<HeroTypingCommandCenterProps>(), {
title: 'Execute workflows at the speed of thought with keyboard command palette.',
description:
'Instant component insertion, theme switching, git branches, and automated deployment pipelines driven by zero-latency hotkeys.',
})
const searchQuery = ref('')
const selectedIndex = ref(0)
const executedAction = ref<CommandAction | null>(null)
const actions: CommandAction[] = [
{
id: 'add-pdp',
title: 'Insert Product Detail Page',
category: 'Components',
shortcut: ['⌘', 'I', 'P'],
icon: Layers,
description: 'Adds flagship unbundled PDP with gallery, reviews & buy box',
status: 'Ready to inject',
},
{
id: 'toggle-dark',
title: 'Toggle High-Contrast Dark Theme',
category: 'Themes',
shortcut: ['⌘', 'T'],
icon: Palette,
description: 'Switches active OKLCH tokens to studio obsidian mode',
status: 'Instant update',
},
{
id: 'git-branch',
title: 'Checkout Release Branch: v2.4.0',
category: 'Git',
shortcut: ['⌘', 'G', 'B'],
icon: FolderGit2,
description: 'Switches workspace to staging pipeline branch',
status: 'Clean working tree',
},
{
id: 'sync-registry',
title: 'Synchronize Dual-Framework ASTs',
category: 'Workflows',
shortcut: ['⌘', 'S', 'Y'],
icon: FileCode,
description: 'Generates parity manifests between Vue 3.5 and React 19',
status: 'Verified 100% parity',
},
{
id: 'run-audit',
title: 'Trigger Automated Craft Audit',
category: 'Workflows',
shortcut: ['⌘', 'A', 'U'],
icon: Zap,
description: 'Validates semantic scales, AA contrast and eliminates design slop',
status: 'Zero regressions',
},
]
const filteredActions = computed(() => {
if (!searchQuery.value.trim()) return actions
const q = searchQuery.value.toLowerCase()
return actions.filter(
(a) =>
a.title.toLowerCase().includes(q) ||
a.category.toLowerCase().includes(q) ||
a.description.toLowerCase().includes(q),
)
})
function execute(action: CommandAction) {
executedAction.value = action
setTimeout(() => {
executedAction.value = null
}, 2500)
}
</script>
<template>
<section
data-slot="hero-typing-command-center"
:class="cn('bg-background relative overflow-hidden py-16 sm:py-24 lg:py-28', props.class)"
>
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 gap-12 lg:grid-cols-12 lg:items-center">
<!-- Left: Headline & Key Advantages (5 Cols) -->
<div class="space-y-6 lg:col-span-5">
<div
class="border-border/80 bg-secondary/60 text-foreground inline-flex items-center gap-2 rounded-full border px-3.5 py-1 text-xs font-medium shadow-2xs"
>
<Command class="text-primary size-3.5" />
<span>Keyboard Ergonomics • Zero Latency</span>
</div>
<h1 class="text-foreground text-3xl font-bold tracking-tight sm:text-4xl lg:text-5xl">
{{ title }}
</h1>
<p class="text-muted-foreground text-base sm:text-lg">
{{ description }}
</p>
<!-- Hotkey Highlights Grid -->
<div class="border-border bg-muted/20 grid grid-cols-2 gap-3 rounded-xl border p-4 text-xs">
<div class="space-y-1">
<div class="text-muted-foreground font-medium">Global Trigger</div>
<div class="flex items-center gap-1 font-mono">
<kbd class="border-border bg-card rounded border px-1.5 py-0.5 shadow-2xs">⌘</kbd>
<kbd class="border-border bg-card rounded border px-1.5 py-0.5 shadow-2xs">K</kbd>
</div>
</div>
<div class="space-y-1">
<div class="text-muted-foreground font-medium">Quick Insert</div>
<div class="flex items-center gap-1 font-mono">
<kbd class="border-border bg-card rounded border px-1.5 py-0.5 shadow-2xs">⌘</kbd>
<kbd class="border-border bg-card rounded border px-1.5 py-0.5 shadow-2xs">I</kbd>
</div>
</div>
</div>
</div>
<!-- Right: Interactive Command Palette Hub (7 Cols) -->
<div class="lg:col-span-7">
<Card class="border-border bg-card shadow-lg">
<!-- Search Header -->
<div class="border-border flex items-center gap-3 border-b px-4 py-3">
<Search class="text-muted-foreground size-4 shrink-0" />
<input
v-model="searchQuery"
type="text"
placeholder="Type a command or search actions (e.g., 'insert', 'git', 'theme')..."
class="placeholder:text-muted-foreground text-foreground flex-1 bg-transparent text-sm outline-none"
/>
<div class="text-muted-foreground flex items-center gap-1 font-mono text-xs">
<kbd class="border-border bg-muted rounded border px-1.5 py-0.5 text-xs">ESC</kbd>
</div>
</div>
<!-- Command List -->
<CardContent class="space-y-1 p-2">
<button
v-for="(action, idx) in filteredActions"
:key="action.id"
type="button"
:class="
cn(
'group flex w-full items-center justify-between rounded-lg px-3 py-2.5 text-left transition-colors',
selectedIndex === idx
? 'border-primary/30 bg-accent text-accent-foreground border'
: 'hover:bg-muted/50 text-foreground border border-transparent',
)
"
@mouseenter="selectedIndex = idx"
@click="execute(action)"
>
<div class="flex min-w-0 items-center gap-3">
<div
class="border-border bg-background flex size-8 shrink-0 items-center justify-center rounded-md border shadow-2xs"
>
<component :is="action.icon" class="text-primary size-4" />
</div>
<div class="min-w-0 space-y-0.5">
<div class="flex items-center gap-2">
<span class="truncate text-xs font-semibold">{{ action.title }}</span>
<Badge variant="outline" class="h-4.5 px-1.5 text-xs">{{ action.category }}</Badge>
</div>
<p class="text-muted-foreground truncate text-xs">{{ action.description }}</p>
</div>
</div>
<!-- Shortcut Badge Group -->
<div class="ml-3 flex shrink-0 items-center gap-1 font-mono text-xs">
<kbd
v-for="(key, kIdx) in action.shortcut"
:key="kIdx"
class="border-border bg-background rounded border px-1.5 py-0.5 text-xs shadow-2xs"
>
{{ key }}
</kbd>
</div>
</button>
</CardContent>
<!-- Status Footer -->
<div class="border-border bg-muted/30 flex items-center justify-between border-t px-4 py-2.5 text-xs">
<div class="text-muted-foreground flex items-center gap-2">
<CornerDownLeft class="size-3.5" />
<span>Press <strong>Enter</strong> to run</span>
</div>
<div v-if="executedAction" class="text-success flex items-center gap-1.5 font-semibold">
<Check class="size-3.5" />
<span>Executed: {{ executedAction.title }}</span>
</div>
<div v-else class="text-muted-foreground font-mono text-xs">
{{ filteredActions.length }} actions available
</div>
</div>
</Card>
</div>
</div>
</div>
</section>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { BookOpen, Maximize2, Pause, Play, Star, Volume2, VolumeX, X } from 'lucide-vue-next'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
interface VideoChapter {
id: string
title: string
timestamp: string
description: string
}
const chapters: VideoChapter[] = [
{
id: 'intro',
title: '1. Architecture & Registry Engine',
timestamp: '0:45',
description: 'Learn how unbundled components provide 100% source ownership without npm dependencies.',
},
{
id: 'tokens',
title: '2. OKLCH Theme & Token System',
timestamp: '1:30',
description: 'Explore the fluid color space engine and dynamic contrast balancing in Tailwind v4.',
},
{
id: 'parity',
title: '3. Dual-Framework Parity Model',
timestamp: '2:15',
description: 'Deep dive into mirror synchronization between Vue 3.5 SFC and React 19 TSX.',
},
]
const isModalOpen = ref(false)
const isPlaying = ref(true)
const isMuted = ref(false)
const playbackSpeed = ref<1 | 1.5 | 2>(1)
const activeChapterIndex = ref(0)
const progressPercent = ref(38)
const currentChapter = computed(() => chapters[activeChapterIndex.value])
function openModal(index = 0) {
activeChapterIndex.value = index
isModalOpen.value = true
isPlaying.value = true
}
function closeModal() {
isModalOpen.value = false
isPlaying.value = false
}
function togglePlay() {
isPlaying.value = !isPlaying.value
}
</script>
<template>
<section
data-slot="hero-video-modal-walkthrough"
class="bg-background relative overflow-hidden px-4 py-16 sm:px-6 sm:py-24 lg:px-8"
>
<!-- Ambient Glow Background -->
<div
class="bg-primary/10 pointer-events-none absolute top-12 left-1/2 -z-10 h-72 w-full max-w-5xl -translate-x-1/2 rounded-full blur-xl"
/>
<div class="mx-auto max-w-6xl space-y-12 text-center">
<!-- Headline & Subtitle -->
<div class="mx-auto max-w-3xl space-y-5">
<Badge variant="secondary" class="gap-1.5 px-3 py-1 font-mono text-xs shadow-xs">
<Play class="text-primary fill-primary/20 size-3.5" />
Interactive 3-Minute Architecture Walkthrough
</Badge>
<h1 class="text-foreground text-4xl leading-[1.12] font-bold tracking-tight sm:text-5xl lg:text-6xl">
See how top engineering teams build with UIPKGE.
</h1>
<p class="text-muted-foreground text-base leading-relaxed sm:text-lg">
Watch a full walkthrough of our design engineering principles, OKLCH token engine, and dual-framework
component architecture.
</p>
<!-- CTA Action Buttons -->
<div class="flex flex-wrap items-center justify-center gap-3 pt-2">
<Button size="lg" class="gap-2 font-semibold shadow-xs" @click="openModal(0)">
<Play class="size-4 fill-current" />
<span>Watch Product Tour (3:30)</span>
</Button>
<Button as="a" href="#docs" variant="outline" size="lg" class="gap-2 font-medium">
<BookOpen class="text-muted-foreground size-4" />
<span>Read Architecture RFC</span>
</Button>
</div>
<!-- Social Proof Avatars & Stars -->
<div class="text-muted-foreground flex flex-wrap items-center justify-center gap-6 pt-3 text-xs">
<div class="flex -space-x-2 overflow-hidden">
<div
class="ring-background bg-primary/20 text-primary flex inline-block size-7 items-center justify-center rounded-full text-xs font-bold ring-2"
>
RF
</div>
<div
class="ring-background bg-success/20 text-success flex inline-block size-7 items-center justify-center rounded-full text-xs font-bold ring-2"
>
PC
</div>
<div
class="ring-background bg-info/20 text-info flex inline-block size-7 items-center justify-center rounded-full text-xs font-bold ring-2"
>
EK
</div>
<div
class="ring-background bg-warning/20 text-warning flex inline-block size-7 items-center justify-center rounded-full text-xs font-bold ring-2"
>
SH
</div>
</div>
<div class="text-warning flex items-center gap-1">
<Star v-for="i in 5" :key="i" class="size-3.5 fill-current" />
<span class="text-foreground ml-1 font-mono text-xs font-medium">4.9/5 by 1,400+ Design Engineers</span>
</div>
</div>
</div>
<!-- Video Preview Card & Chapter Trigger Grid -->
<div class="mx-auto max-w-4xl">
<Card
class="border-border bg-card/95 group cursor-pointer overflow-hidden rounded-2xl p-2 shadow-sm backdrop-blur-md"
@click="openModal(0)"
>
<!-- Video Thumbnail Frame -->
<div
class="bg-muted/60 border-border/80 relative flex aspect-video flex-col justify-between overflow-hidden rounded-xl border p-6 text-left"
>
<!-- Overlay Backdrop Texture -->
<div class="from-background/95 via-background/40 absolute inset-0 z-0 bg-gradient-to-t to-transparent" />
<!-- Top Row: Badge & Length -->
<div class="relative z-10 flex items-center justify-between">
<span
class="bg-background/80 border-border text-foreground inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1 font-mono text-xs font-medium backdrop-blur-md"
>
<span class="bg-success size-2 animate-pulse rounded-full" />
<span>Full HD • 60 FPS</span>
</span>
<span
class="bg-background/80 border-border text-muted-foreground rounded-md border px-2.5 py-1 font-mono text-xs backdrop-blur-md"
>
Total runtime: 3m 30s
</span>
</div>
<!-- Center: Play Button Beacon -->
<div class="relative z-10 flex flex-col items-center justify-center py-8">
<div
class="bg-primary text-primary-foreground shadow-primary/25 flex size-16 items-center justify-center rounded-full shadow-lg transition-transform duration-300 group-hover:scale-110 sm:size-20"
>
<Play class="ml-1 size-7 fill-current sm:size-8" />
</div>
<p class="text-foreground mt-4 text-sm font-semibold tracking-tight">
Click to Launch Interactive Player
</p>
</div>
<!-- Bottom: Chapter Navigation Strip -->
<div class="border-border/60 relative z-10 grid grid-cols-1 gap-2 border-t pt-2 sm:grid-cols-3">
<div
v-for="(chapter, idx) in chapters"
:key="chapter.id"
class="bg-background/60 border-border/50 hover:bg-background/90 rounded-lg border p-2 text-left backdrop-blur-sm transition-colors"
@click.stop="openModal(idx)"
>
<div class="text-muted-foreground flex items-center justify-between font-mono text-xs">
<span class="truncate">{{ chapter.title.split('.')[1] }}</span>
<span class="text-primary font-bold">{{ chapter.timestamp }}</span>
</div>
</div>
</div>
</div>
</Card>
</div>
</div>
<!-- Interactive Video Dialog Modal -->
<div
v-if="isModalOpen"
class="bg-background/80 fixed inset-0 z-50 flex items-center justify-center p-4 backdrop-blur-md sm:p-6"
>
<div
class="bg-card border-border animate-in fade-in-0 zoom-in-95 relative w-full max-w-4xl overflow-hidden rounded-2xl border text-left shadow-sm duration-200"
>
<!-- Modal Top Bar -->
<div class="border-border bg-muted/40 flex items-center justify-between border-b px-4 py-3">
<div class="flex items-center gap-2">
<span class="bg-primary size-2.5 rounded-full" />
<span class="text-foreground font-mono text-xs font-semibold">{{ currentChapter.title }}</span>
</div>
<button
type="button"
class="text-muted-foreground hover:text-foreground hover:bg-muted rounded-md p-1 transition-colors"
@click="closeModal"
>
<X class="size-4" />
</button>
</div>
<!-- Simulated Video Playback Viewport -->
<div class="relative flex aspect-video flex-col justify-between bg-neutral-950 p-6 text-white select-none">
<div class="flex items-center justify-between font-mono text-xs opacity-80">
<span>UIPKGE Architecture Masterclass</span>
<span>OKLCH Engine v2.4</span>
</div>
<!-- Video Mock Graphic -->
<div class="flex flex-col items-center justify-center space-y-3">
<div
class="flex size-16 items-center justify-center rounded-full border border-white/20 bg-white/10 backdrop-blur-md"
>
<Play v-if="!isPlaying" class="ml-1 size-7 fill-current" />
<Pause v-else class="size-7" />
</div>
<p class="max-w-md text-center font-mono text-xs text-neutral-400">
{{ currentChapter.description }}
</p>
</div>
<!-- Timeline Scrubber & Controls -->
<div class="space-y-2">
<!-- Progress Bar -->
<div class="h-1.5 w-full cursor-pointer overflow-hidden rounded-full bg-white/20">
<div
class="bg-primary h-full transition-[width] duration-200"
:style="{ width: `${progressPercent}%` }"
/>
</div>
<!-- Controls Row -->
<div class="flex items-center justify-between font-mono text-xs">
<div class="flex items-center gap-3">
<button type="button" class="hover:text-primary transition-colors" @click="togglePlay">
<Pause v-if="isPlaying" class="size-4" />
<Play v-else class="size-4 fill-current" />
</button>
<button type="button" class="hover:text-primary transition-colors" @click="isMuted = !isMuted">
<VolumeX v-if="isMuted" class="size-4" />
<Volume2 v-else class="size-4" />
</button>
<span class="text-neutral-400">{{ currentChapter.timestamp }} / 3:30</span>
</div>
<div class="flex items-center gap-2">
<button
type="button"
class="rounded bg-white/10 px-2 py-0.5 transition-colors hover:bg-white/20"
@click="playbackSpeed = playbackSpeed === 1 ? 1.5 : playbackSpeed === 1.5 ? 2 : 1"
>
{{ playbackSpeed }}x
</button>
<button type="button" class="hover:text-primary p-1 transition-colors">
<Maximize2 class="size-3.5" />
</button>
</div>
</div>
</div>
</div>
<!-- Modal Chapter Selector Footer -->
<div class="border-border bg-muted/20 grid grid-cols-3 border-t">
<button
v-for="(ch, idx) in chapters"
:key="ch.id"
type="button"
class="border-border border-r p-3 text-left font-mono text-xs transition-colors last:border-r-0"
:class="
activeChapterIndex === idx
? 'bg-background text-foreground font-semibold'
: 'text-muted-foreground hover:text-foreground'
"
@click="activeChapterIndex = idx"
>
<div class="truncate">{{ ch.title }}</div>
<div class="text-muted-foreground mt-0.5 text-xs">{{ ch.timestamp }}</div>
</button>
</div>
</div>
</div>
</section>
</template>
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { ArrowRight, Check, CheckCircle2, Copy, Shield } from 'lucide-vue-next'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
type Role = 'frontend' | 'architect' | 'founder'
interface RoleOption {
id: Role
label: string
perk: string
}
const roleOptions: RoleOption[] = [
{ id: 'frontend', label: 'Frontend / UI Engineer', perk: 'Direct access to raw SFC / TSX templates & Figma tokens' },
{ id: 'architect', label: 'Solutions Architect', perk: 'Full unbundled AST registry spec & self-hosting blueprints' },
{ id: 'founder', label: 'Founder / CTO', perk: 'Zero runtime dependency compliance & white-label enterprise SLA' },
]
const selectedRole = ref<Role>('frontend')
const email = ref('')
const isSubmitted = ref(false)
const queueNumber = ref<number | null>(null)
const referralCopied = ref(false)
// Countdown timer state
const timeLeft = ref({
days: 14,
hours: 8,
minutes: 42,
seconds: 19,
})
let timerInterval: ReturnType<typeof setInterval> | undefined
onMounted(() => {
timerInterval = setInterval(() => {
if (timeLeft.value.seconds > 0) {
timeLeft.value.seconds--
} else {
timeLeft.value.seconds = 59
if (timeLeft.value.minutes > 0) {
timeLeft.value.minutes--
} else {
timeLeft.value.minutes = 59
if (timeLeft.value.hours > 0) {
timeLeft.value.hours--
}
}
}
}, 1000)
})
onUnmounted(() => {
if (timerInterval) clearInterval(timerInterval)
})
function submitWaitlist() {
if (!email.value || !email.value.includes('@')) return
queueNumber.value = Math.floor(Math.random() * 80) + 120
isSubmitted.value = true
}
function copyReferral() {
navigator.clipboard.writeText(`https://uipkge.dev/join?ref=${queueNumber.value || 142}`)
referralCopied.value = true
setTimeout(() => (referralCopied.value = false), 2000)
}
const activeRoleObj = computed(() => roleOptions.find((r) => r.id === selectedRole.value)!)
</script>
<template>
<section
data-slot="hero-waitlist-glow"
class="bg-background relative overflow-hidden px-4 py-16 text-center sm:px-6 sm:py-24 lg:px-8"
>
<!-- Center Radial Glow -->
<div
class="bg-primary/15 pointer-events-none absolute top-1/4 left-1/2 -z-10 h-96 w-full max-w-4xl -translate-x-1/2 -translate-y-1/2 rounded-full blur-xl"
/>
<div class="mx-auto max-w-4xl space-y-10">
<!-- Top Pill -->
<div
class="border-border bg-muted/40 inline-flex items-center gap-2 rounded-full border px-3.5 py-1 font-mono text-xs shadow-xs"
>
<span class="bg-success size-2 rounded-full" />
<span class="text-foreground font-medium">Public Registry Release</span>
<span class="text-muted-foreground">•</span>
<span class="text-primary font-semibold">96% Claimed</span>
</div>
<!-- Main Headline -->
<div class="mx-auto max-w-3xl space-y-4">
<h1 class="text-foreground text-4xl leading-[1.12] font-bold tracking-tight sm:text-5xl lg:text-6xl">
Unbundled UI components and composable application blocks.
</h1>
<p class="text-muted-foreground mx-auto max-w-2xl text-base leading-relaxed sm:text-lg">
Join over thousands of developers building high-velocity web applications with unbundled Vue 3.5 & React 19
components.
</p>
</div>
<!-- Live Countdown Bar -->
<div class="mx-auto grid max-w-md grid-cols-4 gap-3">
<div class="border-border bg-card/60 rounded-xl border p-3 backdrop-blur-sm">
<p class="text-foreground font-mono text-2xl font-bold">{{ String(timeLeft.days).padStart(2, '0') }}</p>
<p class="text-muted-foreground mt-0.5 font-mono text-xs uppercase">Days</p>
</div>
<div class="border-border bg-card/60 rounded-xl border p-3 backdrop-blur-sm">
<p class="text-foreground font-mono text-2xl font-bold">{{ String(timeLeft.hours).padStart(2, '0') }}</p>
<p class="text-muted-foreground mt-0.5 font-mono text-xs uppercase">Hours</p>
</div>
<div class="border-border bg-card/60 rounded-xl border p-3 backdrop-blur-sm">
<p class="text-foreground font-mono text-2xl font-bold">{{ String(timeLeft.minutes).padStart(2, '0') }}</p>
<p class="text-muted-foreground mt-0.5 font-mono text-xs uppercase">Mins</p>
</div>
<div class="border-border bg-card/60 rounded-xl border p-3 backdrop-blur-sm">
<p class="text-foreground font-mono text-2xl font-bold">{{ String(timeLeft.seconds).padStart(2, '0') }}</p>
<p class="text-muted-foreground mt-0.5 font-mono text-xs uppercase">Secs</p>
</div>
</div>
<!-- Interactive Waitlist Workbench Card -->
<Card
class="border-border bg-card/95 mx-auto max-w-2xl overflow-hidden rounded-2xl p-6 text-left shadow-sm backdrop-blur-md sm:p-8"
>
<div v-if="!isSubmitted" class="space-y-6">
<!-- Role Selection Tabs -->
<div class="space-y-2">
<label class="text-muted-foreground font-mono text-xs tracking-wider uppercase">Select Your Track</label>
<div class="grid grid-cols-3 gap-2">
<button
v-for="opt in roleOptions"
:key="opt.id"
type="button"
class="rounded-lg border p-2.5 text-center text-xs font-medium transition-colors"
:class="
selectedRole === opt.id
? 'border-primary bg-primary/10 text-foreground font-semibold shadow-xs'
: 'border-border bg-background/50 text-muted-foreground hover:text-foreground'
"
@click="selectedRole = opt.id"
>
{{ opt.label.split(' ')[0] }}
</button>
</div>
<p class="text-muted-foreground pt-1 text-xs italic">→ {{ activeRoleObj.perk }}</p>
</div>
<!-- Email Input & Submit -->
<form class="space-y-3" @submit.prevent="submitWaitlist">
<div class="flex flex-col items-center gap-2 sm:flex-row">
<Input
v-model="email"
type="email"
placeholder="[email protected]"
required
class="bg-background h-11 font-mono text-sm"
/>
<Button type="submit" size="lg" class="h-11 w-full shrink-0 gap-2 px-6 font-semibold sm:w-auto">
<span>Get Started</span>
<ArrowRight class="size-4" />
</Button>
</div>
<p class="text-muted-foreground flex items-center gap-1.5 text-xs">
<Shield class="text-success size-3.5" />
<span>Zero spam. Instant private registry token delivered upon verification.</span>
</p>
</form>
</div>
<!-- Success Queue State -->
<div v-else class="space-y-5 py-4 text-center">
<div
class="border-success/20 bg-success/10 text-success mx-auto flex size-12 items-center justify-center rounded-full border"
>
<CheckCircle2 class="size-6" />
</div>
<div class="space-y-1">
<h3 class="text-foreground text-xl font-bold">You're on the list!</h3>
<p class="text-primary font-mono text-xs font-semibold">Priority Spot: #{{ queueNumber }} in line</p>
<p class="text-muted-foreground mx-auto max-w-sm text-xs">
We dispatched an invitation link to <span class="text-foreground font-mono">{{ email }}</span
>.
</p>
</div>
<!-- Referral Link Box -->
<div class="border-border bg-muted/30 mx-auto max-w-md space-y-2 rounded-xl border p-3">
<div class="text-muted-foreground flex items-center justify-between font-mono text-xs">
<span>Jump 5 spots per referral</span>
<span class="text-success font-bold">+5 boost</span>
</div>
<div class="flex items-center gap-2">
<Input
readonly
:value="`https://uipkge.dev/join?ref=${queueNumber}`"
class="bg-background h-9 font-mono text-xs"
/>
<Button type="button" size="sm" variant="outline" class="h-9 gap-1.5 px-3" @click="copyReferral">
<Check v-if="referralCopied" class="text-success size-3" />
<Copy v-else class="size-3" />
<span>{{ referralCopied ? 'Copied' : 'Copy' }}</span>
</Button>
</div>
</div>
</div>
</Card>
</div>
</section>
</template>
Raw manifest:https://uipkge.dev/r/vue/hero.json