푸터 조건 단순화 + 심볼 타입 배지 테마 대응

- 푸터를 홈 경로에서만 렌더링 (!fullscreen → isHome)
  이동 시 푸터가 잠깐 보였다 사라지는 플래시 제거
- 심볼 관리 타입 배지(아케인/어센틱/그랜드 어센틱) 테마별 토큰화
  라이트 모드에서 violet/sky/amber-300이 흰 배경에 안 보이던 문제 해결
- 계산기 페이지의 fullscreen 훅을 useLayoutEffect로 변경

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
caadiq 2026-04-19 11:12:19 +09:00
parent e78a18dedb
commit 45d325dfbe
6 changed files with 51 additions and 13 deletions

View file

@ -206,12 +206,15 @@ function HomeLinkButton() {
}
export default function Layout() {
const location = useLocation()
const [fullscreen, setFullscreen] = useState(false)
const [loginOpen, setLoginOpen] = useState(false)
const isAdmin = !!useMatch('/admin/*')
const homeTo = isAdmin ? '/admin' : '/'
const theme = useThemeStore((s) => s.theme)
const isHome = location.pathname === '/'
useEffect(() => {
const root = document.documentElement
if (theme === 'light') root.setAttribute('data-theme', 'light')
@ -256,7 +259,7 @@ export default function Layout() {
}`}>
<Outlet />
</main>
{!fullscreen && <Footer />}
{isHome && <Footer />}
</div>
</LayoutContext.Provider>
)

View file

@ -1,4 +1,4 @@
import { useEffect } from 'react'
import { useEffect, useLayoutEffect } from 'react'
import { useQuery, useQueries } from '@tanstack/react-query'
import { api } from '../../api/client'
import { useLayout } from '../../components/Layout'
@ -21,7 +21,7 @@ export default function BossCrystal() {
// ( + )
const { setFullscreen } = useLayout()
useEffect(() => {
useLayoutEffect(() => {
setFullscreen(true)
return () => setFullscreen(false)
}, [setFullscreen])

View file

@ -1,4 +1,4 @@
import { useState, useEffect } from 'react'
import { useState, useEffect, useLayoutEffect } from 'react'
import { useQuery } from '@tanstack/react-query'
import dayjs from 'dayjs'
import { api } from '../../api/client'
@ -62,7 +62,7 @@ function calcMonthlyEarn(weekData) {
export default function Liberation() {
const { setFullscreen } = useLayout()
useEffect(() => {
useLayoutEffect(() => {
setFullscreen(true)
return () => setFullscreen(false)
}, [setFullscreen])

View file

@ -1,4 +1,4 @@
import { useState, useEffect, useMemo } from 'react'
import { useState, useEffect, useLayoutEffect, useMemo } from 'react'
import { useQuery, useQueries, useMutation } from '@tanstack/react-query'
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
@ -371,7 +371,7 @@ function SymbolCard({ symbol, equipped, charId }) {
export default function Symbol() {
const { setFullscreen } = useLayout()
useEffect(() => {
useLayoutEffect(() => {
setFullscreen(true)
return () => setFullscreen(false)
}, [setFullscreen])

View file

@ -12,14 +12,26 @@ import {
import { CSS } from '@dnd-kit/utilities'
import { api } from '../../../api/client'
const TYPE_COLOR = {
'아케인': { text: 'text-violet-300', bg: 'bg-violet-500/15', border: 'border-violet-500/30' },
'어센틱': { text: 'text-sky-300', bg: 'bg-sky-500/15', border: 'border-sky-500/30' },
'그랜드 어센틱': { text: 'text-amber-300', bg: 'bg-amber-500/15', border: 'border-amber-500/30' },
const TYPE_STYLE = {
'아케인': {
color: 'var(--symbol-arcane-text)',
background: 'var(--symbol-arcane-bg)',
borderColor: 'var(--symbol-arcane-border)',
},
'어센틱': {
color: 'var(--symbol-authentic-text)',
background: 'var(--symbol-authentic-bg)',
borderColor: 'var(--symbol-authentic-border)',
},
'그랜드 어센틱': {
color: 'var(--symbol-grand-text)',
background: 'var(--symbol-grand-bg)',
borderColor: 'var(--symbol-grand-border)',
},
}
function SymbolCardContent({ symbol, dragging = false }) {
const color = TYPE_COLOR[symbol.type] || TYPE_COLOR['아케인']
const badgeStyle = TYPE_STYLE[symbol.type] || TYPE_STYLE['아케인']
return (
<div
className="flex items-stretch rounded-2xl border"
@ -53,7 +65,10 @@ function SymbolCardContent({ symbol, dragging = false }) {
<div className="flex-1 min-w-0">
<div className="flex items-baseline gap-2 flex-wrap">
<h3 className="font-medium truncate">{symbol.region}</h3>
<span className={`text-[10px] font-medium px-1.5 py-0.5 rounded border ${color.text} ${color.bg} ${color.border}`}>
<span
className="text-[10px] font-medium px-1.5 py-0.5 rounded border"
style={badgeStyle}
>
{symbol.type}
</span>
</div>

View file

@ -145,6 +145,16 @@
--liberation-primary-bar: rgba(167, 139, 250, 0.5);
--liberation-secondary: #fda4af;
--liberation-secondary-bar: rgba(253, 164, 175, 0.5);
--symbol-arcane-text: #c4b5fd;
--symbol-arcane-bg: rgba(139, 92, 246, 0.15);
--symbol-arcane-border: rgba(139, 92, 246, 0.3);
--symbol-authentic-text: #7dd3fc;
--symbol-authentic-bg: rgba(14, 165, 233, 0.15);
--symbol-authentic-border: rgba(14, 165, 233, 0.3);
--symbol-grand-text: #fcd34d;
--symbol-grand-bg: rgba(245, 158, 11, 0.15);
--symbol-grand-border: rgba(245, 158, 11, 0.3);
}
/* 테마 토큰 - light */
@ -286,6 +296,16 @@
--liberation-primary-bar: rgba(124, 58, 237, 0.5);
--liberation-secondary: #e11d48;
--liberation-secondary-bar: rgba(225, 29, 72, 0.5);
--symbol-arcane-text: #6d28d9;
--symbol-arcane-bg: rgba(139, 92, 246, 0.12);
--symbol-arcane-border: rgba(109, 40, 217, 0.4);
--symbol-authentic-text: #0369a1;
--symbol-authentic-bg: rgba(14, 165, 233, 0.12);
--symbol-authentic-border: rgba(3, 105, 161, 0.4);
--symbol-grand-text: #b45309;
--symbol-grand-bg: rgba(245, 158, 11, 0.15);
--symbol-grand-border: rgba(180, 83, 9, 0.4);
}
html, body, #root {