Enhance UI components with visual polish, including hover animations and improved skeleton loaders for better user experience

This commit is contained in:
2026-02-01 15:06:49 -05:00
parent abff5a381f
commit 8cf30b5f34
14 changed files with 77 additions and 49 deletions

View File

@@ -56,7 +56,7 @@
@endif
@if($loading)
<div class="h-8 w-20 bg-neutral/20 rounded animate-pulse"></div>
<div class="h-8 w-20 bg-neutral/20 rounded skeleton-shimmer"></div>
@else
<span class="text-2xl font-bold text-base/90 truncate">{{ $value }}</span>
@endif

View File

@@ -39,7 +39,7 @@
@endphp
<div class="relative inline-block" x-data="{ open: false }" @click.outside="open = false" @close.stop="open = false">
<div @click="open = !open">
<div @click="open = !open" class="focus-within:ring-2 focus-within:ring-primary/30 rounded-md">
{{ $trigger }}
</div>

View File

@@ -33,7 +33,7 @@
class="transition-all duration-500 ease-out"
>
@if($icon)
<div class="flex items-center justify-center bg-background-secondary rounded-full transition-transform duration-300 hover:scale-110 {{ $compact ? 'size-11 mb-3' : 'size-16 mb-5' }}"
<div class="flex items-center justify-center bg-background-secondary rounded-full transition-transform duration-300 hover:scale-110 empty-state-float {{ $compact ? 'size-11 mb-3' : 'size-16 mb-5' }}"
:class="loaded ? 'scale-100' : 'scale-90'"
style="transition: transform 0.5s ease-out 0.1s;">
<x-dynamic-component

View File

@@ -78,7 +78,7 @@
@if($statusText)
<div class="px-5 py-3 bg-black/10 text-sm transition-colors duration-200 hover:bg-black/20 flex items-center justify-between">
<span>{{ $statusText }}</span>
<x-ri-arrow-right-line class="size-4" />
<x-ri-arrow-right-line class="size-4 card-arrow-animate" />
</div>
@endif
</{{ $tag }}>

View File

@@ -1,28 +1,28 @@
<div class="space-y-4">
<div class="bg-background-secondary border border-neutral p-4 rounded-lg mb-4 flex flex-col animate-pulse">
<div class="bg-background-secondary border border-neutral p-4 rounded-lg mb-4 flex flex-col">
<div class="flex items-center justify-between mb-2">
<div class="flex items-center gap-3">
<div class="bg-gray-700 p-2 rounded-lg w-8 h-8"></div>
<div class="h-4 w-32 bg-gray-600 rounded"></div>
<div class="bg-gray-700 p-2 rounded-lg w-8 h-8 skeleton-shimmer"></div>
<div class="h-4 w-32 bg-gray-600 rounded skeleton-shimmer"></div>
</div>
<div class="h-5 w-5 bg-gray-600 rounded"></div>
<div class="h-5 w-5 bg-gray-600 rounded skeleton-shimmer"></div>
</div>
<div class="flex flex-row flex-wrap gap-4 mt-4">
<div class="flex flex-col">
<div class="text-xs text-base/50">IP Address</div>
<div class="h-4 w-24 bg-gray-600 rounded"></div>
<div class="h-4 w-24 bg-gray-600 rounded skeleton-shimmer"></div>
</div>
<div class="hidden sm:flex flex-col">
<div class="text-xs text-base/50">Memory</div>
<div class="h-4 w-24 bg-gray-600 rounded"></div>
<div class="h-4 w-24 bg-gray-600 rounded skeleton-shimmer"></div>
</div>
<div class="hidden sm:flex flex-col">
<div class="text-xs text-base/50">Disk</div>
<div class="h-4 w-24 bg-gray-600 rounded"></div>
<div class="h-4 w-24 bg-gray-600 rounded skeleton-shimmer"></div>
</div>
<div class="hidden sm:flex flex-col">
<div class="text-xs text-base/50">CPU</div>
<div class="h-4 w-14 bg-gray-600 rounded"></div>
<div class="h-4 w-14 bg-gray-600 rounded skeleton-shimmer"></div>
</div>
</div>
</div>

View File

@@ -2085,6 +2085,32 @@ img {
box-shadow: 0 0 20px hsl(var(--color-primary) / 0.4);
}
/* =============================================================================
VISUAL POLISH ENHANCEMENTS
============================================================================= */
/* Card arrow hover animation - slides right on hover */
.card-arrow-animate {
transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1),
color 0.15s ease;
}
.group:hover .card-arrow-animate,
a:hover .card-arrow-animate {
transform: translateX(4px);
color: hsl(var(--color-primary));
}
/* Status card hover enhancement - subtle lift and glow */
.status-card-hover {
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.status-card-hover:hover {
transform: scale(1.02);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}
/* Sidebar active indicator bar */
.nav-active-bar {
position: relative;
@@ -2113,6 +2139,8 @@ img {
.ripple::after,
.interactive-lift,
.form-error-shake,
.card-arrow-animate,
.status-card-hover,
[wire\:navigate],
::view-transition-old(page),
::view-transition-new(page) {

View File

@@ -11,7 +11,7 @@
})">
<button
class="flex flex-row items-center px-2 py-1 text-sm font-semibold whitespace-nowrap text-base hover:text-primary transition-colors duration-200"
class="flex flex-row items-center px-2 py-1 text-sm font-semibold whitespace-nowrap text-base hover:text-primary transition-colors duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background rounded-md"
x-on:click="open = !open"
:aria-expanded="open">
{{ $trigger }}

View File

@@ -23,7 +23,7 @@
</legend>
@endif
<textarea type="{{ $type ?? 'text' }}" id="{{ $id ?? $name }}" name="{{ $name }}"
class="block w-full text-sm text-primary-100 bg-primary-800 border-2 border-neutral rounded-md outline-none focus:outline-none focus:border-secondary transition-all duration-300 ease-in-out disabled:bg-primary-700 disabled:cursor-not-allowed {{ $class ?? '' }} @if ($type !== 'color') px-2.5 py-2.5 @endif"
class="block w-full text-sm text-base bg-background-secondary/50 border border-neutral rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary transition-all duration-300 ease-in-out disabled:bg-background-secondary/50 disabled:cursor-not-allowed resize-y min-h-[100px] {{ $class ?? '' }} @if ($type !== 'color') px-2.5 py-2.5 @endif"
placeholder="{{ $placeholder ?? ($label ?? '') }}" @if ($dirty && isset($attributes['wire:model']))
wire:dirty.class="!border-yellow-600" @endif {{ $attributes->except(['placeholder', 'label', 'id', 'name', 'type', 'class', 'divClass', 'required', 'hideRequiredIndicator', 'dirty']) }}
@required($required)>{{ $slot }}</textarea>

View File

@@ -3,9 +3,9 @@
<nav role="navigation" aria-label="Pagination Navigation" class="flex gap-2 items-center">
<span>
@if ($paginator->onFirstPage())
<span class="bg-background-secondary text-base/50 px-4 py-2 rounded-lg cursor-not-allowed border border-neutral transition-colors duration-200">Previous</span>
<span class="bg-background-secondary text-base/50 px-4 py-2 rounded-lg cursor-not-allowed border border-neutral/30 opacity-50 transition-colors duration-200">Previous</span>
@else
<button wire:click="previousPage" wire:loading.attr="disabled" rel="prev" class="bg-background-secondary text-base px-4 py-2 rounded-lg border border-neutral hover:bg-background/80 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-primary/50">Previous</button>
<button wire:click="previousPage" wire:loading.attr="disabled" rel="prev" class="bg-background-secondary text-base px-4 py-2 rounded-lg border border-neutral hover:bg-background/80 hover:border-primary/50 transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-primary/50">Previous</button>
@endif
</span>
@@ -19,16 +19,16 @@
abs($paginator->currentPage() - $page) <= 1)
<span>
<button wire:click="gotoPage({{ $page }})" wire:loading.attr="disabled"
class="{{ $page === $paginator->currentPage() ? 'bg-primary text-white border-primary' : 'bg-background-secondary text-base border-neutral hover:bg-background/80' }} px-4 py-2 rounded-lg cursor-pointer border transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-primary/50">{{ $page }}</button>
class="{{ $page === $paginator->currentPage() ? 'bg-primary text-white border-primary shadow-sm' : 'bg-background-secondary text-base border-neutral hover:bg-background/80 hover:border-primary/50' }} px-4 py-2 rounded-lg cursor-pointer border transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-primary/50">{{ $page }}</button>
</span>
@elseif($page == 3 || $page == $paginator->lastPage() - 3)
<span class="bg-background-secondary text-base/50 px-4 py-2 rounded-lg border border-neutral">
<span class="bg-background-secondary text-base/50 px-4 py-2 rounded-lg border border-neutral/30">
<span>...</span>
</span>
@endif
@endforeach
@else
<span class="bg-background-secondary text-base/50 px-4 py-2 rounded-lg border border-neutral">
<span class="bg-background-secondary text-base/50 px-4 py-2 rounded-lg border border-neutral/30">
<span>...</span>
</span>
@endif
@@ -37,10 +37,10 @@
<span>
@if ($paginator->onLastPage())
<span class="bg-background-secondary text-base/50 px-4 py-2 rounded-lg cursor-not-allowed border border-neutral transition-colors duration-200">Next</span>
<span class="bg-background-secondary text-base/50 px-4 py-2 rounded-lg cursor-not-allowed border border-neutral/30 opacity-50 transition-colors duration-200">Next</span>
@else
<button wire:click="nextPage" wire:loading.attr="disabled" rel="next"
class="bg-background-secondary text-base px-4 py-2 rounded-lg border border-neutral hover:bg-background/80 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-primary/50">Next</button>
class="bg-background-secondary text-base px-4 py-2 rounded-lg border border-neutral hover:bg-background/80 hover:border-primary/50 transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-primary/50">Next</button>
@endif
</span>
</nav>

View File

@@ -1,25 +1,25 @@
{{-- Skeleton loader for invoice card --}}
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 p-6 rounded-xl shadow-lg
flex flex-col md:flex-row items-start md:items-center justify-between gap-4 animate-pulse">
flex flex-col md:flex-row items-start md:items-center justify-between gap-4">
<div class="flex items-start md:items-center gap-4 flex-grow">
{{-- Icon skeleton --}}
<div class="p-3 rounded-full flex-shrink-0 bg-blue-500/20 w-12 h-12"></div>
<div class="p-3 rounded-full flex-shrink-0 bg-blue-500/20 w-12 h-12 skeleton-shimmer"></div>
<div class="flex flex-col flex-grow">
{{-- Title skeleton --}}
<div class="h-6 bg-neutral/20 rounded-lg w-40 mb-2"></div>
<div class="h-6 bg-neutral/20 rounded-lg w-40 mb-2 skeleton-shimmer"></div>
{{-- Amount and date skeleton --}}
<div class="h-4 bg-neutral/20 rounded w-56 mb-1"></div>
<div class="h-4 bg-neutral/20 rounded w-56 mb-1 skeleton-shimmer"></div>
{{-- Due date skeleton --}}
<div class="h-3 bg-neutral/20 rounded w-36"></div>
<div class="h-3 bg-neutral/20 rounded w-36 skeleton-shimmer"></div>
</div>
</div>
<div class="flex items-center gap-3 flex-shrink-0 mt-3 md:mt-0">
{{-- Status badge skeleton --}}
<div class="h-7 bg-neutral/20 rounded-full w-20"></div>
<div class="h-7 bg-neutral/20 rounded-full w-20 skeleton-shimmer"></div>
{{-- Arrow skeleton --}}
<div class="h-6 w-6 bg-neutral/20 rounded"></div>
<div class="h-6 w-6 bg-neutral/20 rounded skeleton-shimmer"></div>
</div>
</div>

View File

@@ -1,25 +1,25 @@
{{-- Skeleton loader for service card --}}
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 p-6 rounded-xl shadow-lg
flex flex-col md:flex-row items-start md:items-center justify-between gap-4 animate-pulse">
flex flex-col md:flex-row items-start md:items-center justify-between gap-4">
<div class="flex items-start md:items-center gap-4 flex-grow">
{{-- Icon skeleton --}}
<div class="p-3 rounded-full flex-shrink-0 bg-neutral/20 w-12 h-12"></div>
<div class="p-3 rounded-full flex-shrink-0 bg-neutral/20 w-12 h-12 skeleton-shimmer"></div>
<div class="flex flex-col flex-grow">
{{-- Title skeleton --}}
<div class="h-6 bg-neutral/20 rounded-lg w-48 mb-2"></div>
<div class="h-6 bg-neutral/20 rounded-lg w-48 mb-2 skeleton-shimmer"></div>
{{-- Description skeleton --}}
<div class="h-4 bg-neutral/20 rounded w-64 mb-1"></div>
<div class="h-4 bg-neutral/20 rounded w-64 mb-1 skeleton-shimmer"></div>
{{-- Date skeleton --}}
<div class="h-3 bg-neutral/20 rounded w-32"></div>
<div class="h-3 bg-neutral/20 rounded w-32 skeleton-shimmer"></div>
</div>
</div>
<div class="flex items-center gap-3 flex-shrink-0 mt-3 md:mt-0">
{{-- Status badge skeleton --}}
<div class="h-7 bg-neutral/20 rounded-full w-20"></div>
<div class="h-7 bg-neutral/20 rounded-full w-20 skeleton-shimmer"></div>
{{-- Arrow skeleton --}}
<div class="h-6 w-6 bg-neutral/20 rounded"></div>
<div class="h-6 w-6 bg-neutral/20 rounded skeleton-shimmer"></div>
</div>
</div>

View File

@@ -5,7 +5,7 @@
</h1>
<div class="grid grid-cols-3 md:grid-cols-3 gap-4 mb-12 bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 p-2 rounded-xl">
<div class="bg-success/10 border border-success/50 rounded-xl p-4 text-center">
<div class="bg-success/10 border border-success/50 rounded-xl p-4 text-center status-card-hover">
<div class="flex items-center justify-center mb-2">
<x-ri-checkbox-circle-fill class="size-6 text-success mr-2" />
<span class="text-2xl font-bold text-success">{{ $invoices->where('status', 'paid')->count() }}</span>
@@ -13,7 +13,7 @@
<p class="text-sm font-medium text-success">{{ __('Paid') }}</p>
</div>
<div class="bg-warning/10 border border-warning/50 rounded-xl p-4 text-center">
<div class="bg-warning/10 border border-warning/50 rounded-xl p-4 text-center status-card-hover">
<div class="flex items-center justify-center mb-2">
<x-ri-error-warning-fill class="size-6 text-warning mr-2" />
<span class="text-2xl font-bold text-warning">{{ $invoices->where('status', 'pending')->count() }}</span>
@@ -21,7 +21,7 @@
<p class="text-sm font-medium text-warning">{{ __('Pending') }}</p>
</div>
<div class="bg-inactive/10 border border-inactive/50 rounded-xl p-4 text-center">
<div class="bg-inactive/10 border border-inactive/50 rounded-xl p-4 text-center status-card-hover">
<div class="flex items-center justify-center mb-2">
<x-ri-close-circle-fill class="size-6 text-inactive mr-2" />
<span class="text-2xl font-bold text-inactive">{{ $invoices->where('status', 'cancelled')->count() }}</span>
@@ -80,13 +80,13 @@
{{ __('Pending') }}
@endif
</div>
<x-ri-arrow-right-s-line class="size-6 text-color-muted group-hover:text-primary transition-colors duration-200" />
<x-ri-arrow-right-s-line class="size-6 text-color-muted card-arrow-animate" />
</div>
</div>
</a>
@empty
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 rounded-xl shadow-lg p-8 lg:p-12 border border-neutral/50 text-center group">
<div class="bg-blue-500/10 rounded-full p-6 w-fit mx-auto mb-6 transition-transform duration-300 group-hover:scale-110">
<div class="bg-blue-500/10 rounded-full p-6 w-fit mx-auto mb-6 transition-transform duration-300 group-hover:scale-110 empty-state-float">
<x-ri-file-list-3-line class="size-12 text-blue-500" />
</div>
<h3 class="text-2xl font-bold text-color-base mb-3">No invoices yet</h3>

View File

@@ -6,7 +6,7 @@
{{-- Status Overview --}}
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-12 bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 p-2 rounded-xl">
<div class="bg-success/10 border border-success/50 rounded-xl p-4 text-center">
<div class="bg-success/10 border border-success/50 rounded-xl p-4 text-center status-card-hover">
<div class="flex items-center justify-center mb-2">
<x-ri-checkbox-circle-fill class="size-6 text-success mr-2" />
<span class="text-2xl font-bold text-success">{{ $services->where('status', 'active')->count() }}</span>
@@ -14,7 +14,7 @@
<p class="text-sm font-medium text-success">{{ __('Active') }}</p>
</div>
<div class="bg-warning/10 border border-warning/50 rounded-xl p-4 text-center">
<div class="bg-warning/10 border border-warning/50 rounded-xl p-4 text-center status-card-hover">
<div class="flex items-center justify-center mb-2">
<x-ri-error-warning-fill class="size-6 text-warning mr-2" />
<span class="text-2xl font-bold text-warning">{{ $services->where('status', 'pending')->count() }}</span>
@@ -22,7 +22,7 @@
<p class="text-sm font-medium text-warning">{{ __('Pending') }}</p>
</div>
<div class="bg-error/10 border border-error/50 rounded-xl p-4 text-center">
<div class="bg-error/10 border border-error/50 rounded-xl p-4 text-center status-card-hover">
<div class="flex items-center justify-center mb-2">
<x-ri-forbid-fill class="size-6 text-red-500 mr-2" />
<span class="text-2xl font-bold text-red-500">{{ $services->where('status', 'suspended')->count() }}</span>
@@ -30,7 +30,7 @@
<p class="text-sm font-medium text-red-500">{{ __('Suspended') }}</p>
</div>
<div class="bg-inactive/10 border border-inactive/50 rounded-xl p-4 text-center">
<div class="bg-inactive/10 border border-inactive/50 rounded-xl p-4 text-center status-card-hover">
<div class="flex items-center justify-center mb-2">
<x-ri-close-circle-fill class="size-6 text-inactive mr-2" />
<span class="text-2xl font-bold text-inactive">{{ $services->where('status', 'cancelled')->count() }}</span>
@@ -121,13 +121,13 @@
{{ __('Pending') }}
@endif
</div>
<x-ri-arrow-right-s-line class="size-6 text-color-muted group-hover:text-primary transition-colors duration-200" />
<x-ri-arrow-right-s-line class="size-6 text-color-muted card-arrow-animate" />
</div>
</div>
</a>
@empty
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 rounded-xl shadow-lg p-8 lg:p-12 border border-neutral/50 text-center group">
<div class="bg-primary/10 rounded-full p-6 w-fit mx-auto mb-6 transition-transform duration-300 group-hover:scale-110">
<div class="bg-primary/10 rounded-full p-6 w-fit mx-auto mb-6 transition-transform duration-300 group-hover:scale-110 empty-state-float">
<x-ri-server-line class="size-12 text-primary" />
</div>
<h3 class="text-2xl font-bold text-color-base mb-3">No services yet</h3>

View File

@@ -57,13 +57,13 @@
{{ ucfirst($ticket->status) }}
@endif
</div>
<x-ri-arrow-right-s-line class="size-6 text-color-muted group-hover:text-primary transition-colors duration-200" />
<x-ri-arrow-right-s-line class="size-6 text-color-muted card-arrow-animate" />
</div>
</div>
</a>
@empty
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 rounded-xl shadow-lg p-8 lg:p-12 border border-neutral/50 text-center">
<div class="bg-secondary/10 rounded-full p-6 w-fit mx-auto mb-6">
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 rounded-xl shadow-lg p-8 lg:p-12 border border-neutral/50 text-center group">
<div class="bg-secondary/10 rounded-full p-6 w-fit mx-auto mb-6 empty-state-float">
<x-ri-customer-service-2-line class="size-12 text-secondary" />
</div>
<h3 class="text-2xl font-bold text-color-base mb-3">No tickets yet</h3>