Forms
Theme Color Picker code
Copy the implementation or inspect every file that belongs to this component unit.
resources/views/components/ui/theme-color-picker.blade.php
@props([
'align' => 'right',
'fullWidth' => false,
'placement' => 'bottom',
])
@php
/*
| Compact theme colour picker bound to the Alpine `theme` store. Shows the
| current palette swatch in a pill trigger and opens a scrollable panel of
| all configured themes.
|
| Usage:
| <x-ui.theme-color-picker />
| <x-ui.theme-color-picker class="block w-full px-3 sm:hidden" align="left" :full-width="true" />
*/
$themes = collect(config('themes.items', []));
$panelId = 'theme-color-picker-'.substr(md5($align.($fullWidth ? 'full' : 'compact')), 0, 8);
$alignClass = $align === 'left'
? ($placement === 'top' ? 'origin-bottom-left start-0' : 'origin-top-left start-0')
: ($placement === 'top' ? 'origin-bottom-right end-0' : 'origin-top-right end-0');
$placementClass = $placement === 'top' ? 'bottom-full mb-1.5' : 'mt-1.5';
$panelZIndexClass = $placement === 'top' ? 'z-[100]' : 'z-50';
$triggerClass = $fullWidth
? 'flex w-full items-center gap-2.5 rounded-lg border border-gray-300 bg-white px-3 py-2 text-left shadow-sm transition hover:border-gray-400 focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:border-gray-700 dark:bg-gray-900 dark:hover:border-gray-600 dark:focus:ring-offset-gray-950'
: 'inline-flex h-9 max-w-[9.5rem] items-center gap-2 rounded-lg border border-gray-300/80 bg-white/90 px-2.5 shadow-sm backdrop-blur-sm transition hover:border-gray-400 hover:bg-white focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 dark:border-gray-700/80 dark:bg-gray-900/90 dark:hover:border-gray-600 dark:hover:bg-gray-900 dark:focus:ring-offset-gray-950';
@endphp
@if ($themes->count() > 1)
<div
x-data="{ open: false }"
@click.outside="open = false"
@keydown.escape.window="open = false"
{{ $attributes->class(['relative']) }}
>
<button
type="button"
@click="open = ! open"
:aria-expanded="open.toString()"
aria-controls="{{ $panelId }}"
aria-haspopup="listbox"
:title="'{{ __('Theme colour') }}: ' + $store.theme.currentColorLabel"
aria-label="{{ __('Theme colour') }}"
@class([$triggerClass])
>
@foreach ($themes as $theme)
<span
x-show="$store.theme.color === @js($theme['value'])"
x-cloak
class="flex min-w-0 flex-1 items-center gap-2"
>
<span
class="h-4 w-4 shrink-0 rounded-md border border-black/10 shadow-sm ring-1 ring-black/5 dark:border-white/10 dark:ring-white/10"
style="background-color: {{ $theme['accent'] }}"
aria-hidden="true"
></span>
<span @class([
'truncate font-semibold text-gray-700 dark:text-gray-200',
'text-base' => $fullWidth,
'text-xs' => ! $fullWidth,
])>{{ __($theme['name']) }}</span>
</span>
@endforeach
<x-ui.icon
name="caret-down"
weight="bold"
:size="$fullWidth ? 'sm' : 'xs'"
class="shrink-0 text-gray-400 transition-transform dark:text-gray-500"
x-bind:class="open && 'rotate-180'"
/>
</button>
<div
id="{{ $panelId }}"
x-show="open"
x-cloak
x-transition:enter="transition ease-out duration-150"
x-transition:enter-start="opacity-0 scale-95 -translate-y-0.5"
x-transition:enter-end="opacity-100 scale-100 translate-y-0"
x-transition:leave="transition ease-in duration-100"
x-transition:leave-start="opacity-100 scale-100 translate-y-0"
x-transition:leave-end="opacity-0 scale-95 -translate-y-0.5"
role="listbox"
aria-label="{{ __('Theme colour') }}"
@class([
'absolute overflow-hidden rounded-xl border border-gray-200/80 bg-white/95 shadow-lg ring-1 ring-black/5 backdrop-blur-lg dark:border-gray-800/80 dark:bg-gray-950/95 dark:ring-white/10',
$panelZIndexClass,
$placementClass,
$alignClass,
'w-full' => $fullWidth,
'w-56' => ! $fullWidth,
])
>
<div class="border-b border-gray-200/80 px-3 py-2 dark:border-gray-800/80">
<p class="text-[11px] font-semibold uppercase tracking-[0.14em] text-gray-500 dark:text-gray-400">{{ __('Theme colour') }}</p>
</div>
<div class="max-h-56 space-y-0.5 overflow-y-auto p-1.5">
@foreach ($themes as $theme)
<button
type="button"
role="option"
:aria-selected="($store.theme.color === @js($theme['value'])).toString()"
@click="$store.theme.setColorTheme(@js($theme['value'])); open = false"
: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 cursor-pointer items-center gap-2.5 rounded-lg border px-2.5 py-2 text-left transition"
>
<span
class="h-6 w-6 shrink-0 rounded-md 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 truncate text-sm font-medium text-gray-900 dark:text-white">{{ __($theme['name']) }}</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>
@endif