This commit is contained in:
2026-02-15 16:49:15 +01:00
parent 7734e53d87
commit 7dbfdab40e
9 changed files with 720 additions and 173 deletions

View File

@@ -1,62 +1,83 @@
@php
$img_src = $art->thumb ?? '';
$img_srcset = $art->thumb_srcset ?? '';
$base_height = 360;
$orig_width = (int) ($art->width ?? 0);
$orig_height = (int) ($art->height ?? 0);
$img_height = $base_height;
$img_width = ($orig_width > 0 && $orig_height > 0)
? (int) round($orig_width * ($base_height / $orig_height))
: 600;
// If a Collection or array was passed accidentally, pick the first item.
if (isset($art) && (is_array($art) || $art instanceof Illuminate\Support\Collection || $art instanceof Illuminate\Database\Eloquent\Collection)) {
$first = null;
if (is_array($art)) {
$first = reset($art);
} elseif ($art instanceof Illuminate\Support\Collection || $art instanceof Illuminate\Database\Eloquent\Collection) {
$first = $art->first();
}
if ($first) {
$art = $first;
}
}
$title = trim((string) ($art->name ?? $art->title ?? 'Untitled artwork'));
$author = trim((string) ($art->uname ?? $art->author_name ?? $art->author ?? 'Skinbase'));
$category = trim((string) ($art->category_name ?? $art->category ?? 'General'));
$license = trim((string) ($art->license ?? 'Standard'));
$resolution = trim((string) ($art->resolution ?? ((isset($art->width, $art->height) && $art->width && $art->height) ? ($art->width . '×' . $art->height) : '')));
$likes = (int) ($art->likes ?? $art->favourites ?? 0);
$downloads = (int) ($art->downloads ?? $art->downloaded ?? 0);
$img_src = (string) ($art->thumb ?? $art->thumbnail_url ?? '/images/placeholder.jpg');
$img_srcset = (string) ($art->thumb_srcset ?? $art->thumbnail_srcset ?? $img_src);
$img_avif_srcset = (string) ($art->thumb_avif_srcset ?? $img_srcset);
$img_webp_srcset = (string) ($art->thumb_webp_srcset ?? $img_srcset);
$img_width = max(1, (int) ($art->width ?? 800));
$img_height = max(1, (int) ($art->height ?? 600));
$contentUrl = $img_src;
$cardUrl = (string) ($art->url ?? '#');
@endphp
<article class="artwork" itemscope itemtype="http://schema.org/Photograph">
<a href="{{ $art->url ?? '#' }}" itemprop="url" class="group relative rounded-2xl overflow-hidden bg-black/10 border border-white/6 shadow-sm hover:shadow-lg transition-shadow">
<article class="nova-card artwork" itemscope itemtype="https://schema.org/ImageObject">
<meta itemprop="name" content="{{ $title }}">
<meta itemprop="contentUrl" content="{{ $contentUrl }}">
<meta itemprop="creator" content="{{ $author }}">
<meta itemprop="license" content="{{ $license }}">
{{-- Category badge --}}
@if(!empty($art->category_name))
<div class="absolute top-3 left-3 z-30 bg-gradient-to-br from-black/65 to-black/40 text-xs text-white px-2 py-1 rounded-md backdrop-blur-sm">{{ $art->category_name }}</div>
<a href="{{ $cardUrl }}" itemprop="url" class="group relative block overflow-hidden rounded-2xl ring-1 ring-white/5 bg-black/20 shadow-lg shadow-black/40 transition-all duration-200 ease-out hover:-translate-y-0.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/70">
@if(!empty($category))
<div class="absolute left-3 top-3 z-30 rounded-md bg-black/55 px-2 py-1 text-xs text-white backdrop-blur-sm">{{ $category }}</div>
@endif
{{-- Image container with subtle overlay; details hidden until hover --}}
<div class="w-full h-48 md:h-56 lg:h-64 bg-neutral-900 relative overflow-hidden">
<div class="nova-card-media relative aspect-[16/10] overflow-hidden bg-neutral-900">
<picture>
<source srcset="{{ $img_avif_srcset }}" type="image/avif">
<source srcset="{{ $img_webp_srcset }}" type="image/webp">
<img
src="{{ $img_src }}"
srcset="{{ $img_srcset }}"
sizes="(max-width: 768px) 50vw, (max-width: 1280px) 33vw, 25vw"
loading="lazy"
decoding="async"
alt="{{ e($art->name) }}"
alt="{{ e($title) }}"
width="{{ $img_width }}"
height="{{ $img_height }}"
class="w-full h-full object-cover transition-transform duration-300 ease-out group-hover:scale-105"
class="h-full w-full object-cover transition-transform duration-200 ease-out group-hover:scale-[1.04]"
itemprop="thumbnailUrl"
/>
</picture>
{{-- Hover overlay: hidden by default, slides up on hover --}}
<div class="absolute inset-0 flex items-end pointer-events-none opacity-0 translate-y-3 group-hover:opacity-100 group-hover:translate-y-0 group-hover:pointer-events-auto transition-all duration-300">
<div class="w-full bg-gradient-to-t from-black/80 via-black/40 to-transparent p-3 flex items-center justify-between gap-3">
<div class="flex items-center gap-3">
@if(!empty($art->author_avatar))
<img src="{{ $art->author_avatar }}" alt="{{ $art->uname }}" class="w-8 h-8 rounded-full border border-white/8 object-cover">
@else
<span class="w-8 h-8 rounded-full bg-neutral-800 border border-white/6 flex items-center justify-center text-xs text-neutral-400">{{ strtoupper(substr($art->uname ?? '-',0,1)) }}</span>
@endif
<div class="text-sm">
<div class="text-white font-semibold leading-tight truncate">{{ $art->uname ?? '—' }}</div>
<div class="text-neutral-300 text-xs truncate">{{ optional(data_get($art, 'created_at'))->format('M j, Y') ?? '' }}</div>
</div>
</div>
<div class="flex items-center gap-3 text-xs text-neutral-300">
<div class="flex items-center gap-1"><i class="fa fa-eye"></i><span class="ml-1">{{ $art->views ?? 0 }}</span></div>
<div class="flex items-center gap-1"><i class="fa fa-heart"></i><span class="ml-1">{{ $art->likes ?? 0 }}</span></div>
</div>
<div class="pointer-events-none absolute inset-x-0 bottom-0 z-20 bg-gradient-to-t from-black/80 via-black/40 to-transparent p-3 backdrop-blur-[2px] opacity-100 transition-opacity duration-200 md:opacity-0 md:group-hover:opacity-100 md:group-focus-visible:opacity-100">
<div class="truncate text-sm font-semibold text-white">{{ $title }}</div>
<div class="mt-1 flex items-center justify-between gap-3 text-xs text-white/80">
<span class="truncate">by {{ $author }}</span>
<span class="shrink-0"> {{ $likes }} · {{ $downloads }}</span>
</div>
<div class="mt-1 text-[11px] text-white/70">
@if($resolution !== '')
{{ $resolution }}
@endif
{{ $category }} {{ $license }}
</div>
</div>
</div>
{{-- Visually-hidden title for accessibility/SEO (details only shown on hover) --}}
<span class="sr-only">{{ $art->name ?? 'Artwork' }}</span>
<span class="sr-only">{{ $title }} by {{ $author }}</span>
</a>
</article>
</article>

View File

@@ -62,15 +62,10 @@
</div>
</div>
<section class="px-6 pb-10 md:px-10">
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
<section class="px-6 pb-10 pt-8 md:px-10" data-nova-gallery data-gallery-type="browse">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6" data-gallery-grid>
@forelse ($artworks as $art)
<a href="{{ $art->url }}" class="group relative rounded-2xl overflow-hidden bg-black/20 border border-white/10 shadow-lg">
<div class="aspect-[16/10] bg-neutral-900">
<img src="{{ $art->thumb ?? '/images/placeholder.jpg' }}" srcset="{{ $art->thumb_srcset ?? ($art->thumb ?? '/images/placeholder.jpg') }}" alt="{{ $art->name ?? 'Artwork' }}" loading="lazy" class="w-full h-full object-cover" />
</div>
<div class="p-3 text-xs text-neutral-400 group-hover:text-white/80">{{ $art->name ?? 'Artwork' }}</div>
</a>
@include('legacy._artwork_card', ['art' => $art])
@empty
<div class="panel panel-default effect2">
<div class="panel-heading"><strong>No Artworks Yet</strong></div>
@@ -81,9 +76,10 @@
@endforelse
</div>
<div class="flex justify-center mt-10">
<div class="flex justify-center mt-10" data-gallery-pagination>
{{ $artworks->withQueryString()->links() }}
</div>
<div class="hidden mt-8" data-gallery-skeleton></div>
</section>
</main>
</div>
@@ -97,5 +93,39 @@
.nb-hero-fade {
background: linear-gradient(180deg, rgba(17,24,39,0) 0%, rgba(7,10,15,0.9) 60%, rgba(7,10,15,1) 100%);
}
[data-nova-gallery].is-enhanced [data-gallery-grid] {
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr));
grid-auto-rows: 8px;
gap: 1rem;
}
@media (min-width: 768px) {
/* From 768px up to ~991px show 2 columns */
[data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
[data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (min-width: 2600px) {
/* Very large displays: 5 columns */
[data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}
[data-nova-gallery].is-enhanced [data-gallery-grid] > .nova-card { margin: 0 !important; }
[data-nova-gallery].is-enhanced [data-gallery-pagination] { display: none; }
[data-gallery-skeleton].is-loading { display: grid !important; grid-template-columns: inherit; gap: 1rem; }
.nova-skeleton-card {
border-radius: 1rem;
min-height: 180px;
background: linear-gradient(110deg, rgba(255,255,255,.06) 8%, rgba(255,255,255,.12) 18%, rgba(255,255,255,.06) 33%);
background-size: 200% 100%;
animation: novaShimmer 1.2s linear infinite;
}
@keyframes novaShimmer {
to { background-position-x: -200%; }
}
</style>
@endpush
@push('scripts')
<script src="/js/legacy-gallery-init.js" defer></script>
@endpush

View File

@@ -76,15 +76,10 @@
</div>
<!-- Grid -->
<section class="px-6 pb-10 md:px-10">
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
<section class="px-6 pb-10 md:px-10" data-nova-gallery data-gallery-type="category-slug">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6" data-gallery-grid>
@forelse ($artworks as $art)
<a href="{{ $art->url }}" class="group relative rounded-2xl overflow-hidden bg-black/20 border border-white/10 shadow-lg">
<div class="aspect-[16/10] bg-neutral-900">
<img src="{{ $art->thumbnail_url ?? '/images/placeholder.jpg' }}" alt="{{ $art->title ?? 'Artwork' }}" loading="lazy" class="w-full h-full object-cover" />
</div>
<div class="p-3 text-xs text-neutral-400 group-hover:text-white/80">{{ $art->title ?? 'Artwork' }}</div>
</a>
@include('legacy._artwork_card', ['art' => $art])
@empty
<div class="panel panel-default effect2">
<div class="panel-heading"><strong>No Artworks Yet</strong></div>
@@ -95,11 +90,12 @@
@endforelse
</div>
<div class="flex justify-center mt-10">
<div class="flex justify-center mt-10" data-gallery-pagination>
@if ($artworks instanceof \Illuminate\Contracts\Pagination\Paginator)
{{ $artworks->links() }}
@endif
</div>
<div class="hidden mt-8" data-gallery-skeleton></div>
</section>
</main>
</div>
@@ -110,32 +106,35 @@
@push('styles')
<style>
.gallery {
columns: 5 260px;
column-gap: 16px;
[data-nova-gallery].is-enhanced [data-gallery-grid] {
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr));
grid-auto-rows: 8px;
gap: 1rem;
}
.artwork {
break-inside: avoid;
margin-bottom: 16px;
@media (min-width: 768px) {
/* From 768px up to ~991px show 2 columns */
[data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 992px) {
.gallery {
columns: 3 220px;
}
@media (min-width: 1024px) {
[data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (max-width: 768px) {
.gallery {
columns: 2 180px;
}
@media (min-width: 2600px) {
/* Very large displays: 5 columns */
[data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}
@media (max-width: 576px) {
.gallery {
columns: 1 100%;
}
[data-nova-gallery].is-enhanced [data-gallery-grid] > .nova-card { margin: 0 !important; }
[data-nova-gallery].is-enhanced [data-gallery-pagination] { display: none; }
[data-gallery-skeleton].is-loading { display: grid !important; grid-template-columns: inherit; gap: 1rem; }
.nova-skeleton-card {
border-radius: 1rem;
min-height: 180px;
background: linear-gradient(110deg, rgba(255,255,255,.06) 8%, rgba(255,255,255,.12) 18%, rgba(255,255,255,.06) 33%);
background-size: 200% 100%;
animation: novaShimmer 1.2s linear infinite;
}
@keyframes novaShimmer {
to { background-position-x: -200%; }
}
.nb-hero-fade {
background: linear-gradient(180deg, rgba(17,24,39,0) 0%, rgba(7,10,15,0.9) 60%, rgba(7,10,15,1) 100%);
@@ -143,3 +142,7 @@
}
</style>
@endpush
@push('scripts')
<script src="/js/legacy-gallery-init.js" defer></script>
@endpush

View File

@@ -89,15 +89,10 @@
</div>
<!-- Artworks gallery (same layout as subcategory pages) -->
<section class="px-6 pb-10 md:px-10">
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
<section class="px-6 pb-10 md:px-10" data-nova-gallery data-gallery-type="content-type">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6" data-gallery-grid>
@forelse ($artworks as $art)
<a href="{{ $art->url }}" class="group relative rounded-2xl overflow-hidden bg-black/20 border border-white/10 shadow-lg">
<div class="aspect-[16/10] bg-neutral-900">
<img src="{{ $art->thumbnail_url ?? '/images/placeholder.jpg' }}" alt="{{ $art->title ?? 'Artwork' }}" loading="lazy" class="w-full h-full object-cover" />
</div>
<div class="p-3 text-xs text-neutral-400 group-hover:text-white/80">{{ $art->title ?? 'Artwork' }}</div>
</a>
@include('legacy._artwork_card', ['art' => $art])
@empty
<div class="panel panel-default effect2">
<div class="panel-heading"><strong>No Artworks Yet</strong></div>
@@ -108,9 +103,10 @@
@endforelse
</div>
<div class="flex justify-center mt-10">
<div class="flex justify-center mt-10" data-gallery-pagination>
{{ $artworks->withQueryString()->links('pagination::tailwind') }}
</div>
<div class="hidden mt-8" data-gallery-skeleton></div>
</section>
</main>
</div>
@@ -121,13 +117,42 @@
@push('styles')
<style>
.gallery { columns: 5 260px; column-gap: 16px; }
.artwork { break-inside: avoid; margin-bottom: 16px; }
@media (max-width: 992px) { .gallery { columns: 3 220px; } }
@media (max-width: 768px) { .gallery { columns: 2 180px; } }
@media (max-width: 576px) { .gallery { columns: 1 100%; } }
[data-nova-gallery].is-enhanced [data-gallery-grid] {
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr));
grid-auto-rows: 8px;
gap: 1rem;
}
@media (min-width: 768px) {
/* From 768px up to ~991px show 2 columns */
[data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
[data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (min-width: 2600px) {
/* Very large displays: 5 columns */
[data-nova-gallery].is-enhanced [data-gallery-grid] { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}
[data-nova-gallery].is-enhanced [data-gallery-grid] > .nova-card { margin: 0 !important; }
[data-nova-gallery].is-enhanced [data-gallery-pagination] { display: none; }
[data-gallery-skeleton].is-loading { display: grid !important; grid-template-columns: inherit; gap: 1rem; }
.nova-skeleton-card {
border-radius: 1rem;
min-height: 180px;
background: linear-gradient(110deg, rgba(255,255,255,.06) 8%, rgba(255,255,255,.12) 18%, rgba(255,255,255,.06) 33%);
background-size: 200% 100%;
animation: novaShimmer 1.2s linear infinite;
}
@keyframes novaShimmer {
to { background-position-x: -200%; }
}
.nb-hero-fade {
background: linear-gradient(180deg, rgba(17,24,39,0) 0%, rgba(7,10,15,0.9) 60%, rgba(7,10,15,1) 100%);
}
</style>
@endpush
@push('scripts')
<script src="/js/legacy-gallery-init.js" defer></script>
@endpush

View File

@@ -72,7 +72,7 @@
<!-- MAIN -->
<main class="flex-1">
<div class="nova-gallery grid grid-cols-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
<div class="nova-gallery grid grid-cols-1 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-5 gap-4">
@foreach($artworks as $art)
@include('legacy._artwork_card', ['art' => $art])
@endforeach