
3D Extruded Buildings & Urban Footprints
Real-world 3D building extrusions with dynamic sunlight shadows, terrain DEM elevations, and pitch/bearing camera controls.
3D perspective tilt workbench cards with dynamic mouse tracking, interactive token efficiency sliders, spring impulse testing, and dual-framework parity inspect.
Also available for React ->$pnpm dlx shadcn-vue@latest add https://uipkge.dev/r/vue/feature-hover-tilt-cards.json$npx shadcn-vue@latest add https://uipkge.dev/r/vue/feature-hover-tilt-cards.json$yarn dlx shadcn-vue@latest add https://uipkge.dev/r/vue/feature-hover-tilt-cards.json$bunx shadcn-vue@latest add https://uipkge.dev/r/vue/feature-hover-tilt-cards.jsonnpx shadcn-vue@latest add @uipkge/feature-hover-tilt-cardsInstalls to:app/components/blocks/<script setup lang="ts">
import { computed, reactive, ref } from 'vue'
import {
Activity,
ArrowRight,
CheckCircle2,
Code2,
Cpu,
Database,
Eye,
Layers,
LayoutGrid,
Maximize2,
Terminal,
Zap,
} from 'lucide-vue-next'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { cn } from '@/lib/utils'
// Interactive card state tracking for 3D perspective effect
const cardRotations = reactive<Record<number, { rx: number; ry: number; active: boolean }>>({
0: { rx: 0, ry: 0, active: false },
1: { rx: 0, ry: 0, active: false },
2: { rx: 0, ry: 0, active: false },
})
function handleMouseMove(e: MouseEvent, index: number) {
const card = e.currentTarget as HTMLElement
const rect = card.getBoundingClientRect()
const x = e.clientX - rect.left
const y = e.clientY - rect.top
const centerX = rect.width / 2
const centerY = rect.height / 2
const rotateX = ((y - centerY) / centerY) * -7
const rotateY = ((x - centerX) / centerX) * 7
cardRotations[index] = { rx: rotateX, ry: rotateY, active: true }
}
function handleMouseLeave(index: number) {
cardRotations[index] = { rx: 0, ry: 0, active: false }
}
// Card 1 Interactive Token Scale Slider
const tokenScale = ref(100)
// Card 2 Interactive Spring Frequency
const springFrequency = ref(180)
const isPulsing = ref(false)
function triggerSpringTest() {
isPulsing.value = true
setTimeout(() => (isPulsing.value = false), 600)
}
// Card 3 Interactive Framework Output Toggle
const activeMirror = ref<'vue' | 'react'>('vue')
// Kept in script: a literal `<script` inside a template interpolation trips
// prettier's HTML parser (it looks like a real tag open).
const mirrorBadges = {
vue: '<script setup lang="ts"> + Reka UI',
react: "'use client' + Radix UI",
} as const
</script>
<template>
<section
data-slot="feature-hover-tilt-cards"
class="bg-background relative overflow-hidden px-4 py-16 sm:px-6 sm:py-24 lg:px-8"
>
<div class="mx-auto max-w-7xl space-y-12">
<!-- Section Header -->
<div class="mx-auto max-w-3xl space-y-4 text-center">
<Badge variant="secondary" class="gap-1.5 px-3 py-1 font-mono text-xs shadow-xs">
<Eye class="text-primary size-3.5" />
Tactile Physics & Surface Depth
</Badge>
<h2 class="text-foreground text-3xl font-bold tracking-tight sm:text-4xl">
Engineered for tactile feedback & micro-interactions.
</h2>
<p class="text-muted-foreground text-base">
Hover over each workbench card to experience hardware-accelerated 3D depth and live interactive physics.
</p>
</div>
<!-- 3D Perspective Tilt Cards Grid -->
<div class="grid grid-cols-1 gap-6 [perspective:1200px] lg:grid-cols-3">
<!-- Card 1: Unbundled Token Inspector -->
<div
class="transition-transform duration-200 ease-out will-change-transform"
:style="{
transform: `perspective(1000px) rotateX(${cardRotations[0].rx}deg) rotateY(${cardRotations[0].ry}deg)`,
}"
@mousemove="(e) => handleMouseMove(e, 0)"
@mouseleave="() => handleMouseLeave(0)"
>
<Card
class="border-border bg-card/95 hover:border-primary/50 relative flex h-full flex-col justify-between space-y-6 overflow-hidden rounded-2xl p-6 text-left shadow-xl transition-[color,background-color,border-color,box-shadow] hover:shadow-sm"
>
<div class="space-y-3">
<div class="flex items-center justify-between">
<div class="bg-primary/10 text-primary flex size-9 items-center justify-center rounded-xl font-mono">
<Layers class="size-4" />
</div>
<Badge variant="outline" class="font-mono text-xs">Zero Bloat</Badge>
</div>
<h3 class="text-foreground font-mono text-lg font-bold">Unbundled Code Ownership</h3>
<p class="text-muted-foreground text-xs leading-relaxed">
AST-level code generation puts pristine component source directly in your repository.
</p>
<!-- Interactive Token Scale Slider -->
<div class="border-border bg-muted/30 mt-4 space-y-2 rounded-xl border p-3.5">
<div class="flex items-center justify-between font-mono text-xs">
<span class="text-muted-foreground">Tree-Shake Efficiency:</span>
<span class="text-success font-bold">{{ tokenScale }}%</span>
</div>
<input
v-model="tokenScale"
type="range"
min="50"
max="100"
class="accent-primary bg-border h-1.5 w-full cursor-pointer rounded-lg"
/>
<p class="text-muted-foreground font-mono text-xs">
Unused variants automatically pruned at compile time.
</p>
</div>
</div>
<div
class="border-border/60 text-muted-foreground flex items-center justify-between border-t pt-4 font-mono text-xs"
>
<span>0 dependencies added</span>
<span class="text-primary font-semibold">✓ Pure Source</span>
</div>
</Card>
</div>
<!-- Card 2: Spring Velocity Simulator -->
<div
class="transition-transform duration-200 ease-out will-change-transform"
:style="{
transform: `perspective(1000px) rotateX(${cardRotations[1].rx}deg) rotateY(${cardRotations[1].ry}deg)`,
}"
@mousemove="(e) => handleMouseMove(e, 1)"
@mouseleave="() => handleMouseLeave(1)"
>
<Card
class="border-border bg-card/95 hover:border-primary/50 relative flex h-full flex-col justify-between space-y-6 overflow-hidden rounded-2xl p-6 text-left shadow-xl transition-[color,background-color,border-color,box-shadow] hover:shadow-sm"
>
<div class="space-y-3">
<div class="flex items-center justify-between">
<div class="bg-warning/10 text-warning flex size-9 items-center justify-center rounded-xl font-mono">
<Zap class="size-4" />
</div>
<Badge variant="outline" class="border-warning/20 bg-warning/10 text-warning font-mono text-xs">
Fluid Motion
</Badge>
</div>
<h3 class="text-foreground font-mono text-lg font-bold">Spring Physics Engine</h3>
<p class="text-muted-foreground text-xs leading-relaxed">
Calibrated cubic-bezier curves for tactile, zero-overshoot micro-interactions.
</p>
<!-- Interactive Spring Trigger Box -->
<div class="border-border bg-muted/30 mt-4 space-y-3 rounded-xl border p-3.5 text-center">
<div
class="bg-primary text-primary-foreground mx-auto flex size-12 items-center justify-center rounded-2xl font-mono text-sm font-bold shadow-md transition-colors duration-300"
:class="isPulsing ? 'bg-success ring-success/30 scale-125 rotate-12 ring-4' : 'scale-100'"
>
<Cpu class="size-5" />
</div>
<Button size="sm" variant="outline" class="h-8 w-full font-mono text-xs" @click="triggerSpringTest">
Trigger Spring Impulse
</Button>
</div>
</div>
<div
class="border-border/60 text-muted-foreground flex items-center justify-between border-t pt-4 font-mono text-xs"
>
<span>Duration: 180ms</span>
<span class="text-success font-semibold">✓ 60fps Native</span>
</div>
</Card>
</div>
<!-- Card 3: Dual-Framework Mirror Comparator -->
<div
class="transition-transform duration-200 ease-out will-change-transform"
:style="{
transform: `perspective(1000px) rotateX(${cardRotations[2].rx}deg) rotateY(${cardRotations[2].ry}deg)`,
}"
@mousemove="(e) => handleMouseMove(e, 2)"
@mouseleave="() => handleMouseLeave(2)"
>
<Card
class="border-border bg-card/95 hover:border-primary/50 relative flex h-full flex-col justify-between space-y-6 overflow-hidden rounded-2xl p-6 text-left shadow-xl transition-[color,background-color,border-color,box-shadow] hover:shadow-sm"
>
<div class="space-y-3">
<div class="flex items-center justify-between">
<div class="bg-info/10 text-info flex size-9 items-center justify-center rounded-xl font-mono">
<LayoutGrid class="size-4" />
</div>
<Badge variant="outline" class="border-info/20 bg-info/10 text-info font-mono text-xs">
1:1 Parity
</Badge>
</div>
<h3 class="text-foreground font-mono text-lg font-bold">Dual-Framework Compiler</h3>
<p class="text-muted-foreground text-xs leading-relaxed">
Identical DOM attributes, variants, and keyboard ergonomics across Vue 3.5 & React 19.
</p>
<!-- Interactive Framework Switcher -->
<div class="border-border bg-muted/30 mt-4 space-y-2 rounded-xl border p-3 font-mono text-xs">
<div class="bg-background border-border flex items-center gap-1 rounded-lg border p-1">
<button
type="button"
class="flex-1 rounded py-1 text-center transition-colors"
:class="
activeMirror === 'vue'
? 'bg-primary text-primary-foreground font-semibold shadow-xs'
: 'text-muted-foreground'
"
@click="activeMirror = 'vue'"
>
Vue 3.5 SFC
</button>
<button
type="button"
class="flex-1 rounded py-1 text-center transition-colors"
:class="
activeMirror === 'react'
? 'bg-primary text-primary-foreground font-semibold shadow-xs'
: 'text-muted-foreground'
"
@click="activeMirror = 'react'"
>
React 19 JSX
</button>
</div>
<p class="text-muted-foreground pt-1 text-xs">
{{ mirrorBadges[activeMirror] }}
</p>
</div>
</div>
<div
class="border-border/60 text-muted-foreground flex items-center justify-between border-t pt-4 font-mono text-xs"
>
<span>AST Verified</span>
<span class="text-primary font-semibold">✓ 100% Match</span>
</div>
</Card>
</div>
</div>
</div>
</section>
</template>
Raw manifest:https://uipkge.dev/r/vue/feature-hover-tilt-cards.json