Skip to main content

Feedback & status

Banner code

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

resources/views/components/ui/banner.blade.php

@props([
    'variant' => 'brand',
    'icon' => null,
    'href' => null,
    'cta' => null,
    'dismissible' => true,
    'storageKey' => null,
])

@php
    /*
    | Announcement / promo banner — a full-width bar for launches, notices or
    | promotions. Distinct from <x-ui.alert> (inline, contextual) and
    | <x-ui.toast> (transient). When `storageKey` is set, a dismissal is
    | remembered in localStorage so the banner stays hidden on return.
    |
    | Variants: brand, accent, dark, neutral
    |
    | Usage:
    |   <x-ui.banner icon="megaphone" href="/changelog" cta="Read more" storage-key="v2-launch">
    |       Harbour v2 is here — faster, themeable and fully documented.
    |   </x-ui.banner>
    */
    $config = [
        'brand' => 'bg-brand-600 text-white',
        'accent' => 'bg-accent-600 text-white',
        'dark' => 'bg-gray-950 text-white',
        'neutral' => 'bg-gray-100 text-gray-900 dark:bg-gray-900 dark:text-white',
    ][$variant] ?? 'bg-brand-600 text-white';

    $isLight = $variant === 'neutral';
    $linkClass = $isLight
        ? 'text-brand-700 dark:text-brand-300 underline-offset-4 hover:underline'
        : 'text-white underline-offset-4 hover:underline';
@endphp

<div
    @if ($dismissible)
        x-data="{
            show: true,
            init() {
                @if ($storageKey)
                    if (localStorage.getItem('banner:{{ $storageKey }}') === 'dismissed') { this.show = false; }
                @endif
            },
            dismiss() {
                this.show = false;
                @if ($storageKey)
                    localStorage.setItem('banner:{{ $storageKey }}', 'dismissed');
                @endif
            },
        }"
        x-show="show"
        x-cloak
    @endif
    role="region"
    aria-label="Announcement"
    {{ $attributes->merge(['class' => 'relative '.$config]) }}
>
    <div class="mx-auto flex max-w-7xl items-center gap-3 px-4 py-2.5 sm:px-6 lg:px-8 {{ $dismissible ? 'pe-10' : '' }}">
        @if ($icon)
            <x-ui.icon :name="$icon" weight="fill" size="base" class="shrink-0 {{ $isLight ? 'text-brand-600 dark:text-brand-400' : 'text-white/90' }}" />
        @endif

        <p class="min-w-0 flex-1 text-sm font-medium {{ $isLight ? '' : 'text-white' }}">
            {{ $slot }}

            @if ($href && $cta)
                <a href="{{ $href }}" class="ms-1 inline-flex items-center gap-1 font-semibold {{ $linkClass }}">
                    {{ $cta }}
                    <x-ui.icon name="arrow-right" weight="bold" size="xs" />
                </a>
            @endif
        </p>
    </div>

    @if ($dismissible)
        <button
            type="button"
            @click="dismiss()"
            class="absolute end-2 top-1/2 -translate-y-1/2 rounded-md p-1.5 opacity-70 transition hover:opacity-100 {{ $isLight ? 'text-gray-600 dark:text-gray-300' : 'text-white' }}"
            aria-label="Dismiss announcement"
        >
            <x-ui.icon name="x" weight="bold" size="sm" />
        </button>
    @endif
</div>