diff --git a/src/App.tsx b/src/App.tsx index 89ffca5..8e6fa62 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -15,6 +15,7 @@ import { TutorialOverlay } from './components/tutorial/TutorialOverlay'; import { useIdleTick } from './hooks/useIdleTick'; import { useGameStore } from './store/useGameStore'; import { trackGameEvent } from './platform/analytics'; +import { useTimeOfDay, getPalette } from './lib/timeOfDay'; const legendaryShake = keyframes` 0%, 100% { transform: translate3d(0, 0, 0); filter: none; } @@ -25,12 +26,12 @@ const legendaryShake = keyframes` 75% { transform: translate3d(-2px, 1px, 0); } `; -const rootStyle = (shaking: boolean) => css` +const rootStyle = (shaking: boolean, background: string) => css` width: 100%; height: 100vh; display: flex; flex-direction: column; - background-color: #f7f8fa; + background-color: ${background}; overflow: hidden; font-family: 'Pretendard', @@ -38,6 +39,7 @@ const rootStyle = (shaking: boolean) => css` BlinkMacSystemFont, sans-serif; animation: ${shaking ? css`${legendaryShake} 0.42s ease-out` : 'none'}; + transition: background-color 1.5s ease-in-out; `; const contentStyle = css` @@ -51,6 +53,8 @@ export function App() { const [shaking, setShaking] = useState(false); const [showIntro, setShowIntro] = useState(() => localStorage.getItem('firstspark-intro-seen') !== '1'); useIdleTick(); + const timeOfDay = useTimeOfDay(); + const palette = getPalette(timeOfDay); const handleIntroDone = useCallback(() => { localStorage.setItem('firstspark-intro-seen', '1'); @@ -79,7 +83,7 @@ export function App() { }, []); return ( -
+
{showIntro && ( = { + dawn: { background: '#fff6ec', contentTint: 'rgba(255, 196, 138, 0.05)' }, + day: { background: '#f7f8fa', contentTint: 'rgba(255, 255, 255, 0)' }, + dusk: { background: '#fff0e6', contentTint: 'rgba(255, 138, 92, 0.06)' }, + night: { background: '#101728', contentTint: 'rgba(124, 138, 255, 0.04)' }, +}; + +export function getTimeOfDay(date: Date = new Date()): TimeOfDay { + const h = date.getHours(); + if (h >= 5 && h < 9) return 'dawn'; + if (h >= 9 && h < 17) return 'day'; + if (h >= 17 && h < 20) return 'dusk'; + return 'night'; +} + +export function getPalette(tod: TimeOfDay): TimeOfDayPalette { + return PALETTES[tod]; +} + +const TICK_MS = 5 * 60 * 1000; + +export function useTimeOfDay(): TimeOfDay { + const [tod, setTod] = useState(() => getTimeOfDay()); + useEffect(() => { + const id = window.setInterval(() => { + setTod((prev) => { + const next = getTimeOfDay(); + return next === prev ? prev : next; + }); + }, TICK_MS); + return () => window.clearInterval(id); + }, []); + return tod; +}