style: Refactor Vue pages and custom layout components to scoped BEM

This commit is contained in:
Daan Meijer
2026-06-15 23:13:54 +02:00
parent 114d0f81a4
commit bf7da48a58
10 changed files with 682 additions and 158 deletions
+109 -27
View File
@@ -147,66 +147,60 @@ function isOwnerUser(userId: number): boolean {
<Head :title="ledger.name" />
<!-- Floating Toast Notifications -->
<div
class="pointer-events-none fixed top-6 right-6 z-50 flex max-w-sm flex-col gap-3"
>
<div class="c-ledger-show__toast-container">
<div
v-for="toast in toasts"
:key="toast.id"
class="pointer-events-auto flex items-center justify-between gap-4 rounded-lg border border-neutral-700/50 bg-neutral-900 px-4 py-3 text-sm text-white shadow-xl"
class="c-ledger-show__toast-item"
>
<span>{{ toast.message }}</span>
<button
@click="toasts = toasts.filter((t) => t.id !== toast.id)"
class="cursor-pointer text-neutral-400 hover:text-white"
class="c-ledger-show__toast-close-btn"
>
</button>
</div>
</div>
<div class="py-12">
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
<div
class="overflow-hidden bg-white shadow-sm sm:rounded-lg dark:bg-gray-800"
>
<div class="p-6 text-gray-900 dark:text-gray-100">
<h3 class="text-lg font-medium">{{ ledger.name }}</h3>
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
<div class="c-ledger-show">
<div class="c-ledger-show__container">
<div class="c-ledger-show__card">
<div class="c-ledger-show__body">
<h3 class="c-ledger-show__title">{{ ledger.name }}</h3>
<p class="c-ledger-show__score">
Score: {{ ledger.score }}
</p>
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
<p class="c-ledger-show__rules">
{{ ledger.rules }}
</p>
<!-- Ledger Descriptive Media -->
<div
v-if="ledger.media && ledger.media.length > 0"
class="mt-4 flex flex-wrap gap-3"
class="c-ledger-show__media-list"
>
<div
v-for="item in ledger.media"
:key="item.id"
class="max-w-[320px] overflow-hidden rounded-md border border-neutral-200 bg-black dark:border-neutral-700"
class="c-ledger-show__media-item"
>
<img
v-if="item.mime_type.startsWith('image/')"
:src="item.url"
class="h-auto max-h-[200px] w-full cursor-pointer object-cover transition-opacity hover:opacity-90"
class="c-ledger-show__media-img"
@click="openLightbox(item.url, item.mime_type)"
/>
<div
v-else-if="item.mime_type.startsWith('video/')"
class="relative cursor-pointer transition-opacity hover:opacity-90"
class="c-ledger-show__media-video-wrapper"
@click="openLightbox(item.url, item.mime_type)"
>
<video
:src="item.url"
class="h-auto max-h-[200px] w-full"
class="c-ledger-show__media-video"
></video>
<div
class="absolute inset-0 flex items-center justify-center bg-black/40 text-2xl font-bold text-white"
>
<div class="c-ledger-show__media-video-overlay">
</div>
</div>
@@ -233,28 +227,116 @@ function isOwnerUser(userId: number): boolean {
<!-- Lightbox Modal -->
<div
v-if="activeLightboxUrl"
class="fixed inset-0 z-50 flex items-center justify-center bg-black/95 p-4"
class="c-ledger-show__lightbox"
@click="closeLightbox"
>
<button
@click="closeLightbox"
class="absolute top-6 right-6 cursor-pointer text-3xl text-white transition-colors duration-200 hover:text-red-500"
class="c-ledger-show__lightbox-close"
>
</button>
<div class="max-h-full max-w-full" @click.stop>
<div class="c-ledger-show__lightbox-content" @click.stop>
<img
v-if="activeLightboxType === 'image'"
:src="activeLightboxUrl"
class="max-h-[90vh] max-w-full rounded object-contain shadow-lg"
class="c-ledger-show__lightbox-img"
/>
<video
v-else-if="activeLightboxType === 'video'"
:src="activeLightboxUrl"
controls
autoplay
class="max-h-[90vh] max-w-full rounded shadow-lg"
class="c-ledger-show__lightbox-video"
></video>
</div>
</div>
</template>
<style scoped>
@reference "../../../css/app.css";
.c-ledger-show__toast-container {
@apply pointer-events-none fixed top-6 right-6 z-50 flex max-w-sm flex-col gap-3;
}
.c-ledger-show__toast-item {
@apply pointer-events-auto flex items-center justify-between gap-4 rounded-lg border border-neutral-700/50 bg-neutral-900 px-4 py-3 text-sm text-white shadow-xl;
}
.c-ledger-show__toast-close-btn {
@apply cursor-pointer text-neutral-400 hover:text-white;
}
.c-ledger-show {
@apply py-12;
}
.c-ledger-show__container {
@apply mx-auto max-w-7xl sm:px-6 lg:px-8;
}
.c-ledger-show__card {
@apply overflow-hidden bg-white shadow-sm sm:rounded-lg dark:bg-gray-800;
}
.c-ledger-show__body {
@apply p-6 text-gray-900 dark:text-gray-100;
}
.c-ledger-show__title {
@apply text-lg font-medium;
}
.c-ledger-show__score {
@apply mt-2 text-sm text-gray-600 dark:text-gray-400;
}
.c-ledger-show__rules {
@apply mt-2 text-sm text-gray-600 dark:text-gray-400;
}
.c-ledger-show__media-list {
@apply mt-4 flex flex-wrap gap-3;
}
.c-ledger-show__media-item {
@apply max-w-[320px] overflow-hidden rounded-md border border-neutral-200 bg-black dark:border-neutral-700;
}
.c-ledger-show__media-img {
@apply h-auto max-h-[200px] w-full cursor-pointer object-cover transition-opacity hover:opacity-90;
}
.c-ledger-show__media-video-wrapper {
@apply relative cursor-pointer transition-opacity hover:opacity-90;
}
.c-ledger-show__media-video {
@apply h-auto max-h-[200px] w-full;
}
.c-ledger-show__media-video-overlay {
@apply absolute inset-0 flex items-center justify-center bg-black/40 text-2xl font-bold text-white;
}
.c-ledger-show__lightbox {
@apply fixed inset-0 z-50 flex items-center justify-center bg-black/95 p-4;
}
.c-ledger-show__lightbox-close {
@apply absolute top-6 right-6 cursor-pointer text-3xl text-white transition-colors duration-200 hover:text-red-500;
}
.c-ledger-show__lightbox-content {
@apply max-h-full max-w-full;
}
.c-ledger-show__lightbox-img {
@apply max-h-[90vh] max-w-full rounded object-contain shadow-lg;
}
.c-ledger-show__lightbox-video {
@apply max-h-[90vh] max-w-full rounded shadow-lg;
}
</style>