Layout primitives
Spotlight Card code
Copy the implementation or inspect every file that belongs to this component unit.
resources/views/components/ui/spotlight-card.blade.php
@props([
'icon' => null,
'iconWeight' => 'duotone',
'title' => null,
'href' => null,
'meta' => null,
])
@php
/*
| Animated sibling of <x-ui.glow-card>. Keeps the cursor-following spotlight,
| and adds a subtle 3D tilt that tracks the pointer plus a lift on hover.
| Renders as an <a> when `href` is set. Tilt is pointer-driven only, so it
| stays still for keyboard/touch users and under reduced motion.
|
| Usage:
| <x-ui.spotlight-card icon="lightning" title="Realtime" href="#">
| Stream events to any endpoint.
| </x-ui.spotlight-card>
*/
$tag = $href ? 'a' : 'div';
@endphp
<{{ $tag }}
@if ($href) href="{{ $href }}" @endif
x-data="{
gx: '50%', gy: '50%', rx: 0, ry: 0,
reduced: window.matchMedia('(prefers-reduced-motion: reduce)').matches,
move($event) {
const r = $el.getBoundingClientRect();
const px = ($event.clientX - r.left) / r.width;
const py = ($event.clientY - r.top) / r.height;
this.gx = (px * 100) + '%';
this.gy = (py * 100) + '%';
if (! this.reduced) {
this.rx = (0.5 - py) * 8;
this.ry = (px - 0.5) * 8;
}
},
reset() { this.rx = 0; this.ry = 0; this.gx = '50%'; this.gy = '50%'; },
}"
@mousemove="move($event)"
@mouseleave="reset()"
style="--glow-x: 50%; --glow-y: 50%"
:style="`--glow-x: ${gx}; --glow-y: ${gy}; transform: perspective(900px) rotateX(${rx}deg) rotateY(${ry}deg)`"
{{ $attributes->merge(['class' => 'group/spot relative block overflow-hidden rounded-2xl border border-gray-200 bg-white p-6 transition-[border-color,box-shadow,transform] duration-300 hover:-translate-y-1 hover:border-brand-300 hover:shadow-xl hover:shadow-brand-500/10 dark:border-gray-800 dark:bg-gray-900 dark:hover:border-brand-700']) }}
>
{{-- Cursor-following spotlight --}}
<div
class="pointer-events-none absolute inset-0 -z-0 opacity-0 transition-opacity duration-300 group-hover/spot:opacity-100"
style="background: radial-gradient(360px circle at var(--glow-x) var(--glow-y), var(--color-brand-500), transparent 40%); mask-image: radial-gradient(360px circle at var(--glow-x) var(--glow-y), black, transparent 40%);"
aria-hidden="true"
></div>
<div class="pointer-events-none absolute inset-px -z-0 rounded-[15px] bg-white dark:bg-gray-900" aria-hidden="true"></div>
<div class="relative z-10" style="transform: translateZ(40px)">
<div class="flex items-start justify-between gap-3">
@if ($icon)
<span class="flex h-11 w-11 items-center justify-center rounded-xl bg-brand-50 text-brand-600 ring-1 ring-brand-100 transition group-hover/spot:scale-110 group-hover/spot:bg-brand-100 dark:bg-brand-950/60 dark:text-brand-400 dark:ring-brand-900/60">
<x-ui.icon :name="$icon" :weight="$iconWeight" size="xl" />
</span>
@endif
@if ($meta)
<span class="text-xs font-medium uppercase tracking-wider text-gray-400 dark:text-gray-500">{{ $meta }}</span>
@endif
@if ($href)
<x-ui.icon name="arrow-up-right" weight="bold" size="base" class="ml-auto text-gray-300 transition group-hover/spot:translate-x-0.5 group-hover/spot:-translate-y-0.5 group-hover/spot:text-brand-500 dark:text-gray-600" />
@endif
</div>
@if ($title)
<h3 class="mt-4 text-base font-semibold tracking-tight text-gray-900 dark:text-white">{{ $title }}</h3>
@endif
<div class="mt-1.5 text-sm leading-relaxed text-gray-600 dark:text-gray-400">
{{ $slot }}
</div>
</div>
</{{ $tag }}>