Content & marketing
Hero code
Copy the implementation or inspect every file that belongs to this component unit.
resources/views/components/ui/hero.blade.php
@props([
'announce' => null,
'announceHref' => '#',
'title',
'description' => null,
'grid' => true,
'randomGlow' => false,
])
@php
/*
| Landing-page hero. Optional `announce` renders a pill link above the
| headline. Place CTA buttons in the `actions` slot and a product mockup in
| the `media` slot (rendered full-bleed below the copy with a gradient base).
| Set `grid` to false to drop the grid backdrop and keep just the glow.
| Set `randomGlow` to nudge the glow position once on load (skipped when the
| visitor prefers reduced motion).
|
| Usage:
| <x-ui.hero announce="v1.0 is live" title="Ship faster" description="…">
| <x-slot:actions>…</x-slot:actions>
| <x-slot:media><x-ui.window>…</x-ui.window></x-slot:media>
| </x-ui.hero>
*/
$glowClipPath = 'polygon(74% 44%, 100% 61%, 97% 26%, 85% 0, 80% 2%, 72% 32%, 60% 62%, 52% 68%, 47% 58%, 45% 34%, 27% 76%, 0 64%, 17% 100%, 27% 76%, 76% 97%, 74% 44%)';
@endphp
<section
{{ $attributes->merge(['class' => 'relative overflow-hidden']) }}
@if ($randomGlow)
x-data="{
glowX: 50,
glowY: 0,
pedestalInset: 32,
init() {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
return;
}
this.glowX = 38 + Math.random() * 24;
this.glowY = -18 + Math.random() * 36;
this.pedestalInset = 24 + Math.random() * 16;
},
}"
@endif
>
{{-- Grid backdrop + soft radial glow --}}
@if ($grid)
<div class="pointer-events-none absolute inset-0 -z-10 bg-grid opacity-[0.4] [mask-image:radial-gradient(ellipse_60%_50%_at_50%_0%,black,transparent)]" aria-hidden="true"></div>
@endif
<div class="pointer-events-none absolute inset-x-0 -top-40 -z-10 transform-gpu overflow-hidden blur-3xl" aria-hidden="true">
<div
@class([
'relative aspect-[1155/678] w-[36rem] bg-gradient-to-tr from-brand-400 to-brand-600 opacity-20 sm:w-[72rem]',
'left-1/2 -translate-x-1/2' => ! $randomGlow,
])
@if ($randomGlow)
:style="`left: ${glowX}%; transform: translateX(-50%) translateY(${glowY}px); clip-path: {{ $glowClipPath }}`"
@else
style="clip-path: {{ $glowClipPath }}"
@endif
></div>
</div>
<div class="mx-auto max-w-3xl px-4 sm:px-6 lg:px-8 pt-20 pb-16 sm:pt-28 text-center">
@if ($announce)
<div class="mb-8 flex justify-center pt-1">
<a href="{{ $announceHref }}" class="group relative inline-flex items-center gap-2 overflow-visible rounded-full border border-gray-200 dark:border-gray-800 bg-white/70 dark:bg-gray-900/70 backdrop-blur px-3 py-1.5 text-sm text-gray-600 dark:text-gray-300 shadow-sm transition hover:border-brand-300 dark:hover:border-brand-800">
<span class="pointer-events-none absolute left-0 top-0 z-10 -translate-x-1/2 -translate-y-1/2 inline-flex items-center gap-1 rounded-full bg-brand-600 px-2 py-0.5 text-xs font-semibold text-white shadow-sm ring-2 ring-white dark:ring-gray-900">
<x-ui.icon name="sparkle" weight="fill" size="xs" /> New
</span>
<span>{{ $announce }}</span>
<x-ui.icon name="arrow-right" weight="bold" size="xs" class="text-gray-400 transition group-hover:translate-x-0.5" />
</a>
</div>
@endif
<h1 class="text-4xl sm:text-6xl font-semibold tracking-tight text-gray-900 dark:text-white text-balance">
{{ $title }}
</h1>
@if ($description)
<p class="mt-6 text-lg sm:text-xl leading-relaxed text-gray-600 dark:text-gray-400 text-pretty">{{ $description }}</p>
@endif
@isset($actions)
<div class="mt-10 flex flex-col sm:flex-row items-center justify-center gap-3">{{ $actions }}</div>
@endisset
</div>
@isset($media)
<div class="relative mx-auto max-w-6xl px-4 sm:px-6 lg:px-8 pb-20">
{{-- gradient pedestal behind the mockup --}}
<div
@class([
'pointer-events-none absolute top-10 -z-10 h-full rounded-[2rem] bg-gradient-to-b from-brand-500/20 to-transparent blur-2xl',
'inset-x-8' => ! $randomGlow,
])
@if ($randomGlow)
:style="`left: ${pedestalInset}px; right: ${pedestalInset}px`"
@endif
aria-hidden="true"
></div>
{{ $media }}
</div>
@endisset
</section>