테마 전환 타이밍 튜닝

- 라이트 모드 새로고침 시 FOUC 방지 (index.html 블로킹 스크립트)
- 헤더 배경 제거 + backdrop-blur만 유지 → 배경과 동시 전환
- 전환 시간 300ms → 500ms로 일관되게 느리게

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
caadiq 2026-04-17 22:11:27 +09:00
parent 7020794add
commit 749e77774a
5 changed files with 31 additions and 44 deletions

View file

@ -9,6 +9,16 @@
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;700;900&display=swap" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/gh/fonts-archive/Maplestory/Maplestory.css" rel="stylesheet" />
<script type="text/javascript" src="https://openapi.nexon.com/js/analytics.js?app_id=274844" async></script>
<script>
(function () {
try {
var raw = localStorage.getItem('maple-theme');
if (!raw) return;
var theme = JSON.parse(raw).state && JSON.parse(raw).state.theme;
if (theme === 'light') document.documentElement.setAttribute('data-theme', 'light');
} catch (e) {}
})();
</script>
<title>메이플스토리 유틸리티</title>
</head>
<body>

View file

@ -1,7 +1,7 @@
export default function Footer() {
return (
<footer
className="border-t mt-16 transition-colors duration-300"
className="border-t mt-16 transition-colors duration-500"
style={{ borderColor: 'var(--header-border)' }}
>
<div className="mx-auto max-w-5xl px-6 py-8 space-y-4">
@ -11,7 +11,7 @@ export default function Footer() {
</div>
<div
className="grid gap-2 sm:grid-cols-2 text-xs transition-colors duration-300"
className="grid gap-2 sm:grid-cols-2 text-xs transition-colors duration-500"
style={{ color: 'var(--text-dim)' }}
>
<div className="space-y-1">

View file

@ -42,7 +42,7 @@ function CurrentMenuTitle() {
return (
<div
className="flex items-center gap-3 transition-colors duration-300"
className="flex items-center gap-3 transition-colors duration-500"
style={{ color: 'var(--text-muted)' }}
>
<span style={{ color: 'var(--text-slash)' }}>/</span>
@ -51,7 +51,7 @@ function CurrentMenuTitle() {
<img src={menu.image.url} alt="" className="w-5 h-5 object-contain" />
)}
<span
className="text-sm font-medium transition-colors duration-300"
className="text-sm font-medium transition-colors duration-500"
style={{ color: 'var(--text-emphasis)' }}
>
{menu.title}
@ -66,13 +66,7 @@ function ThemeToggle() {
const toggleTheme = useThemeStore((s) => s.toggleTheme)
const isLight = theme === 'light'
const handleToggle = () => {
if (typeof document !== 'undefined' && document.startViewTransition) {
document.startViewTransition(() => toggleTheme())
} else {
toggleTheme()
}
}
const handleToggle = () => toggleTheme()
return (
<button
@ -80,7 +74,7 @@ function ThemeToggle() {
onClick={handleToggle}
aria-label={isLight ? '다크 모드로 전환' : '라이트 모드로 전환'}
title={isLight ? '다크 모드' : '라이트 모드'}
className="relative inline-flex h-8 w-14 items-center rounded-full border transition-colors duration-300 hover:border-emerald-500/40"
className="relative inline-flex h-8 w-14 items-center rounded-full border transition-colors duration-500 hover:border-emerald-500/40"
style={{
background: 'var(--toggle-bg)',
borderColor: 'var(--toggle-border)',
@ -123,15 +117,14 @@ export default function Layout() {
return (
<LayoutContext.Provider value={{ fullscreen, setFullscreen }}>
<div
className={`min-w-[1280px] flex flex-col transition-colors duration-300 ${
className={`min-w-[1280px] flex flex-col transition-colors duration-500 ${
fullscreen ? 'h-dvh' : 'min-h-screen'
}`}
style={{ color: 'var(--text-strong)' }}
>
<header
className="sticky top-0 z-20 border-b backdrop-blur-md shrink-0 transition-colors duration-300"
className="sticky top-0 z-20 border-b backdrop-blur-md shrink-0 transition-colors duration-500"
style={{
background: 'var(--header-bg)',
borderColor: 'var(--header-border)',
}}
>

View file

@ -101,7 +101,9 @@ html, body, #root {
background-color: var(--bg-from);
background-image: linear-gradient(to bottom right, var(--bg-from), var(--bg-via), var(--bg-to));
background-attachment: fixed;
transition: background-color 400ms ease, background-image 400ms ease;
transition:
background-color 500ms cubic-bezier(0.4, 0, 0.2, 1),
background-image 500ms cubic-bezier(0.4, 0, 0.2, 1);
}
html {
overscroll-behavior-y: contain;
@ -160,24 +162,6 @@ input[type="number"] {
to { opacity: 1; transform: translateY(0); }
}
/* 테마 전환 뷰 트랜지션 - 부드러운 크로스페이드 */
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 400ms;
animation-timing-function: ease;
}
::view-transition-old(root) {
animation-name: themeFadeOut;
}
::view-transition-new(root) {
animation-name: themeFadeIn;
}
@keyframes themeFadeOut {
to { opacity: 0; }
}
@keyframes themeFadeIn {
from { opacity: 0; }
}
/* 커스텀 스크롤바 (테마별) */

View file

@ -14,17 +14,17 @@ export default function Home() {
{/* 구분선 */}
<div className="flex items-center gap-4">
<div
className="h-px flex-1 transition-colors duration-300"
className="h-px flex-1 transition-colors duration-500"
style={{ backgroundImage: 'linear-gradient(to right, transparent, var(--divider-line), transparent)' }}
/>
<span
className="text-xs uppercase tracking-widest transition-colors duration-300"
className="text-xs uppercase tracking-widest transition-colors duration-500"
style={{ color: 'var(--text-dim)' }}
>
Utilities
</span>
<div
className="h-px flex-1 transition-colors duration-300"
className="h-px flex-1 transition-colors duration-500"
style={{ backgroundImage: 'linear-gradient(to right, transparent, var(--divider-line), transparent)' }}
/>
</div>
@ -36,19 +36,19 @@ export default function Home() {
{Array.from({ length: 3 }).map((_, i) => (
<div
key={i}
className="h-32 rounded-2xl animate-pulse transition-colors duration-300"
className="h-32 rounded-2xl animate-pulse transition-colors duration-500"
style={{ background: 'var(--skeleton-bg)' }}
/>
))}
</div>
) : menus.length === 0 ? (
<div
className="rounded-2xl border p-16 text-center transition-colors duration-300"
className="rounded-2xl border p-16 text-center transition-colors duration-500"
style={{ background: 'var(--empty-bg)', borderColor: 'var(--empty-border)' }}
>
<div className="text-5xl mb-4 opacity-50">🍁</div>
<p
className="transition-colors duration-300"
className="transition-colors duration-500"
style={{ color: 'var(--text-muted)' }}
>
아직 등록된 기능이 없습니다
@ -76,7 +76,7 @@ export default function Home() {
<div>
<h2 className="font-medium">{menu.title}</h2>
<p
className="text-sm mt-1 leading-relaxed transition-colors duration-300"
className="text-sm mt-1 leading-relaxed transition-colors duration-500"
style={{ color: 'var(--text-muted)' }}
>
{menu.description}
@ -92,17 +92,17 @@ export default function Home() {
{/* 구분선 */}
<div className="flex items-center gap-4">
<div
className="h-px flex-1 transition-colors duration-300"
className="h-px flex-1 transition-colors duration-500"
style={{ backgroundImage: 'linear-gradient(to right, transparent, var(--divider-line), transparent)' }}
/>
<span
className="text-xs uppercase tracking-widest transition-colors duration-300"
className="text-xs uppercase tracking-widest transition-colors duration-500"
style={{ color: 'var(--text-dim)' }}
>
Notices
</span>
<div
className="h-px flex-1 transition-colors duration-300"
className="h-px flex-1 transition-colors duration-500"
style={{ backgroundImage: 'linear-gradient(to right, transparent, var(--divider-line), transparent)' }}
/>
</div>