diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 517d73e..7243537 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -9,6 +9,7 @@ import Header from "@/shared/Header"; import Footer from "@/shared/Footer"; import SideBar from "@/shared/SideBar"; import { ReactQueryDevtools } from '@tanstack/react-query-devtools' +import { Splash } from "@/components/Splash"; import QueryProvider from "@/config/QueryProvider"; import ToastContainer from "@/components/Toast"; @@ -44,6 +45,7 @@ export default function RootLayout({ className={`${geistSans.variable} ${geistMono.variable} antialiased`} > +
diff --git a/src/components/Splash.module.css b/src/components/Splash.module.css new file mode 100644 index 0000000..3ef537e --- /dev/null +++ b/src/components/Splash.module.css @@ -0,0 +1,53 @@ +.splashContainer { + position: fixed; + inset: 0; + z-index: 50; + display: flex; + align-items: center; + justify-content: center; + background-color: white; + will-change: opacity; + animation: fadeIn 0.8s cubic-bezier(0.4, 0, 0.2, 1); +} + +.splashContainer.fadeOut { + will-change: opacity; + animation: fadeOut 0.8s cubic-bezier(0.4, 0, 0.2, 1) forwards; +} + +.logoContainer { + position: relative; + width: 8rem; + height: 8rem; + will-change: transform; + animation: scaleUp 1.5s cubic-bezier(0.4, 0, 0.2, 1) forwards; + transform: translateZ(0); + backface-visibility: hidden; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes fadeOut { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +@keyframes scaleUp { + 0% { + transform: translateZ(0) scale(0.5); + } + 100% { + transform: translateZ(0) scale(1.5); + } +} diff --git a/src/components/Splash.tsx b/src/components/Splash.tsx index 6f7d832..8882219 100644 --- a/src/components/Splash.tsx +++ b/src/components/Splash.tsx @@ -1,36 +1,50 @@ 'use client'; import { useEffect, useState } from 'react'; -import LogoAnimation from './LogoAnimation'; +import Image from 'next/image'; +import { useMediaQuery } from '@/hooks/useMediaQuery'; +import styles from './Splash.module.css'; -const Splash = () => { - const [fadeOut, setFadeOut] = useState(false); - const [mounted, setMounted] = useState(false); +export const Splash = () => { + const [isVisible, setIsVisible] = useState(true); + const [isFading, setIsFading] = useState(false); + const isMobile = useMediaQuery('(max-width: 768px)'); useEffect(() => { - setMounted(true); - const timer = setTimeout(() => { - setFadeOut(true); - }, 2000); - return () => clearTimeout(timer); - }, []); + const hasShown = sessionStorage.getItem('splashShown'); - useEffect(() => { - if (fadeOut) { - document.body.style.overflow = 'auto'; + if (isMobile && !hasShown) { + // Show splash immediately + requestAnimationFrame(() => { + setIsVisible(true); + const delayTimer = setTimeout(() => { + setIsFading(true); + const fadeTimer = setTimeout(() => { + setIsVisible(false); + sessionStorage.setItem('splashShown', 'true'); + }, 800); + return () => clearTimeout(fadeTimer); + }, 6000); + + return () => clearTimeout(delayTimer); + }); } else { - document.body.style.overflow = 'hidden'; + setIsVisible(false); } - }, [fadeOut]); + }, [isMobile]); - if (!mounted) return null; + if (!isVisible) return null; return ( -
-
- +
+
+ Logo
); -}; - -export default Splash; \ No newline at end of file +}; \ No newline at end of file diff --git a/src/hooks/useMediaQuery.ts b/src/hooks/useMediaQuery.ts new file mode 100644 index 0000000..2f0e869 --- /dev/null +++ b/src/hooks/useMediaQuery.ts @@ -0,0 +1,23 @@ +import { useEffect, useState } from "react"; + +export const useMediaQuery = (query: string): boolean => { + const [matches, setMatches] = useState(false); + + useEffect(() => { + const media = window.matchMedia(query); + + // Set initial value + setMatches(media.matches); + + // Create event listener + const listener = (e: MediaQueryListEvent) => setMatches(e.matches); + + // Add listener + media.addEventListener("change", listener); + + // Clean up + return () => media.removeEventListener("change", listener); + }, [query]); + + return matches; +}; diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..bba2235 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,33 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + './src/pages/**/*.{js,ts,jsx,tsx,mdx}', + './src/components/**/*.{js,ts,jsx,tsx,mdx}', + './src/app/**/*.{js,ts,jsx,tsx,mdx}', + ], + theme: { + extend: { + keyframes: { + fadeIn: { + '0%': { opacity: '0' }, + '100%': { opacity: '1' }, + }, + fadeOut: { + '0%': { opacity: '1' }, + '100%': { opacity: '0' }, + }, + scaleUp: { + '0%': { transform: 'scale(0.5)' }, + '50%': { transform: 'scale(1.2)' }, + '100%': { transform: 'scale(1)' }, + }, + }, + animation: { + fadeIn: 'fadeIn 0.5s ease-in-out', + fadeOut: 'fadeOut 0.5s ease-in-out', + scaleUp: 'scaleUp 1s ease-out', + }, + }, + }, + plugins: [], +}; \ No newline at end of file