페이지 전환 깜빡임 완화 및 레이아웃 통합 (v1.10.50)

- 게시물 상세를 default 레이아웃으로 통합해 홈↔게시물 레이아웃 교체 제거
- CSS 전환 베일·스크롤 정리 플러그인으로 이동 중 깜빡임 완화

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-06 14:28:24 +09:00
parent 021c1ee526
commit 4c8f6e44f6
9 changed files with 293 additions and 84 deletions

92
app.vue
View File

@@ -18,68 +18,7 @@ const siteAccentStyle = computed(() => ({
'--site-accent': siteAccentColor.value
}))
const isSitePageNavigating = ref(false)
if (import.meta.client) {
const nuxtApp = useNuxtApp()
const router = useRouter()
/** @type {ReturnType<typeof setTimeout> | null} */
let hideOverlayTimer = null
let navigationOverlayToken = 0
/**
* 공개 페이지 이동 로딩 오버레이를 표시한다.
* @returns {void}
*/
const showSitePageNavigationOverlay = () => {
navigationOverlayToken += 1
clearTimeout(hideOverlayTimer)
hideOverlayTimer = null
isSitePageNavigating.value = true
}
/**
* 새 화면이 실제로 그려진 뒤 공개 페이지 이동 로딩 오버레이를 숨긴다.
* @returns {void}
*/
const scheduleHideSitePageNavigationOverlay = () => {
const token = navigationOverlayToken
requestAnimationFrame(() => {
requestAnimationFrame(() => {
hideOverlayTimer = setTimeout(() => {
if (token !== navigationOverlayToken) {
return
}
isSitePageNavigating.value = false
hideOverlayTimer = null
}, 120)
})
})
}
/**
* 오류·취소 상황에서 공개 페이지 이동 로딩 오버레이를 즉시 숨긴다.
* @returns {void}
*/
const hideSitePageNavigationOverlayImmediately = () => {
clearTimeout(hideOverlayTimer)
hideOverlayTimer = null
isSitePageNavigating.value = false
}
router.beforeEach((to, from) => {
if (to.fullPath !== from.fullPath) {
showSitePageNavigationOverlay()
}
})
router.onError(hideSitePageNavigationOverlayImmediately)
nuxtApp.hook('page:loading:start', showSitePageNavigationOverlay)
nuxtApp.hook('page:finish', scheduleHideSitePageNavigationOverlay)
nuxtApp.hook('app:error', hideSitePageNavigationOverlayImmediately)
}
const isSitePageNavigating = useState('site-page-veil', () => false)
useHead(() => ({
titleTemplate: (titleChunk) => titleChunk || appSiteSettings.value.title,
@@ -97,27 +36,18 @@ useHead(() => ({
<template>
<div class="site-app" :style="siteAccentStyle">
<NuxtLoadingIndicator :color="siteAccentColor" :height="3" :throttle="80" :duration="5000" />
<Transition
enter-active-class="transition duration-100 ease-out"
enter-from-class="opacity-0"
enter-to-class="opacity-100"
leave-active-class="transition duration-150 ease-in"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
<div
v-show="isSitePageNavigating"
class="site-page-loading pointer-events-none fixed inset-0 z-[2147483647] grid place-items-center text-[var(--site-text)]"
role="status"
aria-live="polite"
aria-busy="true"
>
<div
v-if="isSitePageNavigating"
class="site-page-loading fixed inset-0 z-[90] grid place-items-center bg-[var(--site-bg)] text-[var(--site-text)]"
role="status"
aria-live="polite"
>
<div class="site-page-loading__panel grid place-items-center gap-3 text-center">
<span class="site-page-loading__spinner size-8 rounded-full border-2 border-[var(--site-line)] border-t-[var(--site-accent)]" aria-hidden="true" />
<span class="text-xs font-semibold uppercase tracking-[0.18em] opacity-70">Loading</span>
</div>
<div class="site-page-loading__panel grid place-items-center gap-3 text-center">
<span class="site-page-loading__spinner size-8 rounded-full border-2 border-[var(--site-line)] border-t-[var(--site-accent)]" aria-hidden="true" />
<span class="text-xs font-semibold uppercase tracking-[0.18em] opacity-70">Loading</span>
</div>
</Transition>
</div>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>