Skip to main content

Content & marketing

Navbar code

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

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

@props([
    'links' => [],
    'sticky' => true,
])

@php
    /*
    | Marketing top navigation. Pass `links` as [label => href] for flat links or
    | [label => [childLabel => href, ...]] for dropdowns. Use the `brand` slot
    | for a logo and the `actions` slot for auth buttons.
    |
    | Usage:
    |   <x-ui.navbar :links="[
    |       'Features' => '#features',
    |       'Explore' => ['Motion' => route('foundations.motion'), 'Components' => route('components.index')],
    |   ]">
    |       <x-slot:brand>...</x-slot:brand>
    |       <x-slot:actions>...</x-slot:actions>
    |   </x-ui.navbar>
    */
    $isNavLinkActive = function (string $href): bool {
        if (str_starts_with($href, '#')) {
            return request()->routeIs('home');
        }

        $current = rtrim(url()->current(), '/');
        $target = rtrim(url($href), '/');

        if ($current === $target) {
            return true;
        }

        return $target !== '' && str_starts_with($current.'/', $target.'/');
    };

    $navLinkClasses = function (bool $active): string {
        return $active
            ? 'text-sm font-semibold text-brand-600 dark:text-brand-400 transition'
            : 'text-sm font-medium text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition';
    };

    $mobileNavLinkClasses = function (bool $active): string {
        return $active
            ? 'block rounded-lg px-3 py-2 text-base font-semibold text-brand-600 dark:text-brand-400 bg-brand-50 dark:bg-brand-950/40 transition'
            : 'block rounded-lg px-3 py-2 text-base font-medium text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-900 dark:hover:text-white transition';
    };

    $mobileChildNavLinkClasses = function (bool $active): string {
        return $active
            ? 'block rounded-lg py-2 pl-6 pr-3 text-base font-semibold text-brand-600 dark:text-brand-400 bg-brand-50 dark:bg-brand-950/40 transition'
            : 'block rounded-lg py-2 pl-6 pr-3 text-base font-medium text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-900 dark:hover:text-white transition';
    };
@endphp

<nav
    x-data="{ open: false }"
    {{ $attributes->merge(['class' => ($sticky ? 'sticky top-0 ' : '').'z-40 border-b border-gray-200/70 dark:border-gray-800/70 bg-white/80 dark:bg-gray-950/80 backdrop-blur-lg']) }}
>
    <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
        <div class="flex h-16 items-center justify-between gap-4">
            <div class="flex items-center gap-2">
                @isset($brand)
                    {{ $brand }}
                @endisset
            </div>

            <div class="hidden md:flex md:items-center md:gap-8">
                @foreach ($links as $label => $link)
                    @php
                        $isDropdown = is_array($link);
                        $href = $isDropdown ? null : $link;
                        $children = $isDropdown ? $link : [];
                        $active = $isDropdown
                            ? collect($children)->contains(fn (string $childHref): bool => $isNavLinkActive($childHref))
                            : $isNavLinkActive($link);
                    @endphp

                    @if ($isDropdown)
                        <x-ui.dropdown align="left" width="56" variant="nav">
                            <x-slot:trigger>
                                <button type="button" class="inline-flex cursor-pointer items-center gap-1 {{ $navLinkClasses($active) }}">
                                    {{ $label }}
                                    <x-ui.icon name="caret-down" weight="bold" size="xs" class="opacity-70" />
                                </button>
                            </x-slot:trigger>

                            @foreach ($children as $childLabel => $childHref)
                                <x-ui.dropdown.item :href="$childHref" :active="$isNavLinkActive($childHref)">
                                    {{ $childLabel }}
                                </x-ui.dropdown.item>
                            @endforeach
                        </x-ui.dropdown>
                    @else
                        <a
                            href="{{ $href }}"
                            @if ($active) aria-current="page" @endif
                            class="{{ $navLinkClasses($active) }}"
                        >{{ $label }}</a>
                    @endif
                @endforeach
            </div>

            <div class="hidden md:flex md:items-center md:gap-2">
                <x-theme-switcher />
                @isset($actions)
                    {{ $actions }}
                @endisset
            </div>

            <div class="flex items-center gap-1 md:hidden">
                <x-theme-switcher />
                <x-ui.icon-button icon="list" label="Open menu" @click="open = ! open" x-show="! open" />
                <x-ui.icon-button icon="x" label="Close menu" @click="open = false" x-show="open" x-cloak />
            </div>
        </div>
    </div>

    {{-- Mobile menu --}}
    <div x-show="open" x-collapse x-cloak class="md:hidden border-t border-gray-200 dark:border-gray-800">
        <div class="space-y-1 px-4 py-4">
            @foreach ($links as $label => $link)
                @php
                    $isDropdown = is_array($link);
                    $href = $isDropdown ? null : $link;
                    $children = $isDropdown ? $link : [];
                    $active = $isDropdown
                        ? collect($children)->contains(fn (string $childHref): bool => $isNavLinkActive($childHref))
                        : $isNavLinkActive($link);
                @endphp

                @if ($isDropdown)
                    <div x-data="{ expanded: @js($active) }">
                        <button
                            type="button"
                            @click="expanded = ! expanded"
                            class="flex w-full cursor-pointer items-center justify-between rounded-lg px-3 py-2 text-left {{ $active ? 'text-base font-semibold text-brand-600 dark:text-brand-400 bg-brand-50 dark:bg-brand-950/40' : 'text-base font-medium text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-900 dark:hover:text-white' }} transition"
                            :aria-expanded="expanded.toString()"
                        >
                            <span>{{ $label }}</span>
                            <x-ui.icon
                                name="caret-down"
                                weight="bold"
                                size="sm"
                                class="opacity-70 transition-transform"
                                x-bind:class="expanded && 'rotate-180'"
                            />
                        </button>

                        <div x-show="expanded" x-collapse class="space-y-1 pt-1">
                            @foreach ($children as $childLabel => $childHref)
                                @php($childActive = $isNavLinkActive($childHref))
                                <a
                                    href="{{ $childHref }}"
                                    @if ($childActive) aria-current="page" @endif
                                    class="{{ $mobileChildNavLinkClasses($childActive) }}"
                                >{{ $childLabel }}</a>
                            @endforeach
                        </div>
                    </div>
                @else
                    <a
                        href="{{ $href }}"
                        @if ($active) aria-current="page" @endif
                        class="{{ $mobileNavLinkClasses($active) }}"
                    >{{ $label }}</a>
                @endif
            @endforeach

            @if (count(config('themes.items', [])) > 1)
                {{-- Theme colour selector: the top-bar switcher only shows the dark-mode
                     toggle on mobile, so expose the colour picker here in the menu. --}}
                <div class="mt-3 border-t border-gray-200 pt-3 dark:border-gray-800">
                    <x-ui.theme-color-picker align="left" :full-width="true" class="px-3 sm:hidden" />
                </div>
            @endif

            @isset($actions)
                <div class="mt-3 flex flex-col gap-2 border-t border-gray-200 dark:border-gray-800 pt-3">{{ $actions }}</div>
            @endisset
        </div>
    </div>
</nav>