Skip to main content

Content & marketing

Parallax Background code

Copy the implementation or inspect every file that belongs to this component unit.

resources/views/components/ui/parallax-background.blade.php

@props([
    'image',
    'alt' => '',
    'eyebrow' => null,
    'title' => null,
    'description' => null,
    'height' => 'screen',
    'align' => 'center',
    'speed' => 28,
    'overlayClass' => 'bg-gradient-to-br from-gray-950/80 via-gray-950/35 to-gray-950/75',
])

@php
    /*
    | Full-screen parallax image background. Place primary page content in the
    | default slot and CTA buttons in the `actions` slot. Use `height="preview"`
    | when rendering inside documentation panes.
    |
    | Usage:
    |   <x-ui.parallax-background image="/images/forest.jpg" title="Explore">
    |       <x-slot:actions>...</x-slot:actions>
    |   </x-ui.parallax-background>
    */
    $heights = [
        'screen' => 'min-h-screen',
        'large' => 'min-h-[42rem]',
        'preview' => 'min-h-[32rem]',
    ];

    $alignments = [
        'left' => 'items-start text-left',
        'center' => 'items-center text-center',
    ];

    $heightClass = $heights[$height] ?? $height;
    $alignmentClass = $alignments[$align] ?? $alignments['center'];
@endphp

<section
    x-data="{
        mx: 0, my: 0, sy: 0,
        reduced: window.matchMedia('(prefers-reduced-motion: reduce)').matches,
        move($event) {
            if (this.reduced) {
                return;
            }

            const rect = $el.getBoundingClientRect();
            const px = (($event.clientX - rect.left) / rect.width) - 0.5;
            const py = (($event.clientY - rect.top) / rect.height) - 0.5;

            this.mx = px * {{ (int) $speed }};
            this.my = py * {{ (int) $speed }};
        },
        reset() {
            this.mx = 0;
            this.my = 0;
        },
        scroll() {
            if (this.reduced) {
                this.sy = 0;

                return;
            }

            const rect = $el.getBoundingClientRect();
            this.sy = rect.top * -0.08;
        },
    }"
    x-init="scroll()"
    @scroll.window.passive="scroll()"
    @mousemove="move($event)"
    @mouseleave="reset()"
    {{ $attributes->merge(['class' => "relative isolate flex {$heightClass} overflow-hidden bg-gray-950"]) }}
>
    <img
        src="{{ $image }}"
        alt="{{ $alt }}"
        @if ($alt === '') aria-hidden="true" @endif
        class="pointer-events-none absolute inset-0 -z-20 h-full w-full scale-[1.12] object-cover will-change-transform motion-reduce:scale-100 motion-reduce:transform-none"
        style="transform: translate3d(0, 0, 0) scale(1.12);"
        :style="reduced ? 'transform: translate3d(0, 0, 0) scale(1);' : `transform: translate3d(${mx}px, calc(${sy}px + ${my}px), 0) scale(1.12);`"
    >

    <div class="absolute inset-0 -z-10 {{ $overlayClass }}" aria-hidden="true"></div>
    <div class="absolute inset-x-0 bottom-0 -z-10 h-40 bg-gradient-to-t from-gray-950/90 to-transparent" aria-hidden="true"></div>

    <div class="mx-auto flex w-full max-w-7xl px-6 py-16 sm:px-8 lg:px-10">
        <div class="flex w-full flex-col justify-center {{ $alignmentClass }}">
            <div class="max-w-4xl">
                @if ($eyebrow)
                    <x-ui.badge variant="brand" icon="mountains" class="bg-white/12 text-white ring-1 ring-white/20 backdrop-blur dark:bg-white/12 dark:text-white">
                        {{ $eyebrow }}
                    </x-ui.badge>
                @endif

                @if ($title)
                    <h1 class="mt-5 text-4xl font-semibold tracking-tight text-white text-balance sm:text-6xl">
                        {{ $title }}
                    </h1>
                @endif

                @if ($description)
                    <p class="mt-5 max-w-2xl text-base leading-7 text-gray-100/85 text-pretty sm:text-lg {{ $align === 'center' ? 'mx-auto' : '' }}">
                        {{ $description }}
                    </p>
                @endif

                @if ($slot->isNotEmpty())
                    <div class="mt-6 text-base leading-7 text-gray-100/85 {{ $align === 'center' ? 'mx-auto max-w-2xl' : 'max-w-2xl' }}">
                        {{ $slot }}
                    </div>
                @endif

                @isset($actions)
                    <div class="mt-8 flex flex-col gap-3 sm:flex-row {{ $align === 'center' ? 'items-center justify-center' : 'items-start justify-start' }}">
                        {{ $actions }}
                    </div>
                @endisset
            </div>
        </div>
    </div>
</section>