General
Theme Dock code
Copy the implementation or inspect every file that belongs to this component unit.
resources/views/components/ui/theme-dock.blade.php
@props([
'title' => 'Appearance',
])
@php
/*
| Fixed bottom-right preferences menu with light/dark mode and theme colour
| controls. Mount once per page that needs a floating appearance panel.
|
| Usage:
| <x-ui.theme-dock />
*/
$themes = collect(config('themes.items', []));
$panelId = 'theme-dock-panel-'.substr(md5($title), 0, 8);
@endphp
<div
x-data="{ open: false }"
@click.outside="open = false"
@keydown.escape.window="open = false"
{{ $attributes->class(['fixed bottom-6 right-6 z-50']) }}
>
<div
id="{{ $panelId }}"
x-show="open"
x-cloak
x-transition:enter="transition ease-out duration-150"
x-transition:enter-start="translate-y-2 opacity-0"
x-transition:enter-end="translate-y-0 opacity-100"
x-transition:leave="transition ease-in duration-100"
x-transition:leave-start="translate-y-0 opacity-100"
x-transition:leave-end="translate-y-2 opacity-0"
role="dialog"
aria-label="{{ $title }}"
class="absolute bottom-14 right-0 mb-2 w-72 overflow-hidden rounded-2xl border border-gray-200 bg-white shadow-xl ring-1 ring-black/5 dark:border-gray-800 dark:bg-gray-900 dark:ring-white/10"
>
<div class="border-b border-gray-200 px-4 py-3 dark:border-gray-800">
<p class="text-sm font-semibold text-gray-900 dark:text-white">{{ $title }}</p>
<p class="mt-0.5 text-xs text-gray-500 dark:text-gray-400">{{ __('Preview components in any mode or palette.') }}</p>
</div>
<div class="space-y-5 p-4">
<div>
<p class="text-[11px] font-semibold uppercase tracking-[0.14em] text-gray-500 dark:text-gray-400">{{ __('Mode') }}</p>
<div class="mt-2 grid grid-cols-2 gap-1 rounded-xl bg-gray-100 p-1 dark:bg-gray-800">
<button
type="button"
@click="$store.theme.setMode('light')"
:class="$store.theme.current === 'light'
? 'bg-white text-gray-900 shadow-sm dark:bg-gray-950 dark:text-white'
: 'text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200'"
class="inline-flex items-center justify-center gap-1.5 rounded-lg px-3 py-2 text-xs font-semibold transition"
>
<x-ui.icon name="sun" weight="fill" size="sm" />
{{ __('Light') }}
</button>
<button
type="button"
@click="$store.theme.setMode('dark')"
:class="$store.theme.current === 'dark'
? 'bg-white text-gray-900 shadow-sm dark:bg-gray-950 dark:text-white'
: 'text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200'"
class="inline-flex items-center justify-center gap-1.5 rounded-lg px-3 py-2 text-xs font-semibold transition"
>
<x-ui.icon name="moon" weight="fill" size="sm" />
{{ __('Dark') }}
</button>
</div>
</div>
<div>
<p class="text-[11px] font-semibold uppercase tracking-[0.14em] text-gray-500 dark:text-gray-400">{{ __('Theme colour') }}</p>
<div class="mt-2 max-h-52 space-y-1 overflow-y-auto pe-0.5">
@foreach ($themes as $theme)
<button
type="button"
@click="$store.theme.setColorTheme(@js($theme['value']))"
:class="$store.theme.color === @js($theme['value'])
? 'border-brand-300 bg-brand-50/80 dark:border-brand-800 dark:bg-brand-950/40'
: 'border-transparent hover:bg-gray-50 dark:hover:bg-gray-800/80'"
class="flex w-full items-center gap-3 rounded-xl border px-3 py-2 text-left transition"
>
<span
class="h-8 w-8 shrink-0 rounded-lg border border-black/10 shadow-sm dark:border-white/10"
style="background-color: {{ $theme['accent'] }}"
aria-hidden="true"
></span>
<span class="min-w-0 flex-1">
<span class="block truncate text-sm font-medium text-gray-900 dark:text-white">{{ __($theme['name']) }}</span>
<span class="block truncate text-xs text-gray-500 dark:text-gray-400">{{ $theme['role'] ?? '' }}</span>
</span>
<span
x-show="$store.theme.color === @js($theme['value'])"
x-cloak
>
<x-ui.icon
name="check-circle"
weight="fill"
size="sm"
class="shrink-0 text-brand-600 dark:text-brand-400"
/>
</span>
</button>
@endforeach
</div>
</div>
</div>
</div>
<button
type="button"
@click="open = ! open"
:aria-expanded="open.toString()"
aria-controls="{{ $panelId }}"
aria-haspopup="dialog"
:aria-label="open ? '{{ __('Close appearance menu') }}' : '{{ __('Open appearance menu') }}'"
:class="open ? 'bg-gray-900 text-white dark:bg-white dark:text-gray-900' : 'bg-white text-gray-700 dark:bg-gray-900 dark:text-gray-100'"
class="inline-flex h-12 w-12 items-center justify-center rounded-full border border-gray-200 shadow-lg transition hover:scale-105 focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:border-gray-700 dark:ring-offset-gray-950"
>
<x-ui.icon name="swatches" weight="duotone" size="lg" />
</button>
</div>