diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 474be41..8f39513 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -5,7 +5,6 @@ import { useAuth } from '@/contexts/auth'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Skeleton } from '@/components/ui/skeleton'; import { useIsMobile } from '@/hooks/use-mobile'; -import { isIOSPlatform } from '@/utils/platform'; const Header: React.FC = () => { const { @@ -15,9 +14,6 @@ const Header: React.FC = () => { const [imageLoaded, setImageLoaded] = useState(false); const [imageError, setImageError] = useState(false); const isMobile = useIsMobile(); - - // iOS 기기에서는 상단 안전 영역을 위한 클래스 적용 - const safeAreaClass = isIOSPlatform() ? 'needs-top-safe-area' : ''; // 이미지 프리로딩 처리 useEffect(() => { @@ -32,27 +28,16 @@ const Header: React.FC = () => { }; }, []); - return ( -
+ return
- {!imageLoaded && !imageError ? ( -
+ {!imageLoaded && !imageError ?
-
- ) : ( - <> - setImageLoaded(true)} - onError={() => setImageError(true)} - /> +
: <> + setImageLoaded(true)} onError={() => setImageError(true)} /> {(imageError || !imageLoaded) && ZY} - - )} + }

@@ -65,8 +50,7 @@ const Header: React.FC = () => {

-
- ); +
; }; export default Header; diff --git a/src/components/SafeAreaContainer.tsx b/src/components/SafeAreaContainer.tsx deleted file mode 100644 index 53aee63..0000000 --- a/src/components/SafeAreaContainer.tsx +++ /dev/null @@ -1,33 +0,0 @@ - -import React from 'react'; -import { isIOSPlatform } from '@/utils/platform'; - -interface SafeAreaContainerProps { - children: React.ReactNode; - className?: string; - applyTop?: boolean; - applyBottom?: boolean; -} - -/** - * 안전 영역(Safe Area)을 고려한 컨테이너 컴포넌트 - * iOS의 다이나믹 아일랜드/노치와 하단 홈 인디케이터를 고려하여 패딩을 적용합니다. - */ -const SafeAreaContainer: React.FC = ({ - children, - className = '', - applyTop = true, - applyBottom = false -}) => { - // iOS 플랫폼인 경우에만 안전 영역 패딩 적용 - const topPadding = applyTop && isIOSPlatform() ? 'pt-safe-area' : ''; - const bottomPadding = applyBottom && isIOSPlatform() ? 'pb-safe-area' : ''; - - return ( -
- {children} -
- ); -}; - -export default SafeAreaContainer; diff --git a/src/index.css b/src/index.css index e6e075f..3a6b7ce 100644 --- a/src/index.css +++ b/src/index.css @@ -1,84 +1,190 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); + @tailwind base; @tailwind components; @tailwind utilities; -/* Define the glassmorphism effect */ -.glass { - background: rgba(255, 255, 255, 0.2); - border-radius: 16px; - box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); - backdrop-filter: blur(5px); - -webkit-backdrop-filter: blur(5px); - border: 1px solid rgba(255, 255, 255, 0.3); -} -/* Define the neumorphism effect */ -.neuro { - background: #E0E5EC; - border-radius: 10px; - box-shadow: -5px -5px 10px #FFFFFF, - 5px 5px 10px #BABECC; -} -/* Neumorphism text */ -.neuro-text { - color: #666; - text-shadow: -1px -1px 1px #fff, 1px 1px 1px #babebc; -} -/* Flat neumorphism effect (pressed) */ -.neuro-flat { - border-radius: 10px; - background: #E0E5EC; - box-shadow: inset -5px -5px 10px #FFFFFF, - inset 5px 5px 10px #BABECC; -} -/* Skeleton styles */ -.skeleton { - animation: skeleton-loading 1.2s linear infinite alternate; -} -@keyframes skeleton-loading { - 0% { - background-color: hsl(200, 20%, 70%); +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + + --card: 0 0% 100%; + --card-foreground: 222.2 84% 4.9%; + + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 222.2 84% 4.9%; + + --radius: 1rem; + + --sidebar-background: 0 0% 98%; + + --sidebar-foreground: 240 5.3% 26.1%; + + --sidebar-primary: 240 5.9% 10%; + + --sidebar-primary-foreground: 0 0% 98%; + + --sidebar-accent: 240 4.8% 95.9%; + + --sidebar-accent-foreground: 240 5.9% 10%; + + --sidebar-border: 220 13% 91%; + + --sidebar-ring: 217.2 91.2% 59.8%; } - 100% { - background-color: hsl(200, 20%, 95%); + .dark { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 11.2%; + + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 212.7 26.8% 83.9%; + --sidebar-background: 240 5.9% 10%; + --sidebar-foreground: 240 4.8% 95.9%; + --sidebar-primary: 224.3 76.3% 48%; + --sidebar-primary-foreground: 0 0% 100%; + --sidebar-accent: 240 3.7% 15.9%; + --sidebar-accent-foreground: 240 4.8% 95.9%; + --sidebar-border: 240 3.7% 15.9%; + --sidebar-ring: 217.2 91.2% 59.8%; } } -/* 안전 영역 (Safe Area) 관련 유틸리티 클래스 */ -:root { - --safe-area-top: 0px; - --safe-area-bottom: 0px; +@layer base { + * { + @apply border-border; + } + + body { + @apply bg-neuro-background text-foreground font-inter antialiased; + } + + html, body, #root { + @apply h-full overflow-x-hidden; + } } -.pt-safe-area { - padding-top: var(--safe-area-top); +@layer components { + .neuro-flat { + @apply bg-neuro-background shadow-neuro-flat rounded-xl; + } + + .neuro-pressed { + @apply bg-neuro-background shadow-neuro-pressed rounded-xl; + } + + .neuro-convex { + @apply bg-neuro-background shadow-neuro-convex rounded-xl; + } + + .neuro-text { + @apply font-medium tracking-wide; + } + + .page-transition-enter { + @apply animate-fade-in; + } + + .glass-effect { + @apply bg-white/10 backdrop-blur-lg border border-white/20 rounded-xl; + } + + .neuro-button { + @apply neuro-flat px-4 py-3 text-neuro-accent font-medium transition-all duration-200 + hover:shadow-neuro-convex hover:text-neuro-accent-light active:shadow-neuro-pressed; + } + + .neuro-card { + @apply neuro-flat p-6 transition-all duration-300 hover:shadow-neuro-convex; + } + + .neuro-input { + @apply neuro-pressed px-4 py-3 w-full focus:outline-none focus:ring-2 focus:ring-neuro-accent/30; + } + + /* 모바일 화면에서의 추가 스타일 */ + @media (max-width: 768px) { + .neuro-card { + @apply w-full; + } + + #root { + @apply p-0; + } + + /* 모바일에서 팝업과 다이얼로그 스타일 보정 */ + [role="dialog"] { + @apply rounded-xl overflow-hidden; + } + + /* 다이얼로그 내용에 적용되는 스타일 */ + .DialogContent, + .PopoverContent, + .AlertDialogContent, + .DrawerContent, + .SheetContent { + @apply rounded-xl overflow-hidden; + } + } + + /* 데스크탑 화면에서의 추가 스타일 */ + @media (min-width: 769px) { + #root { + @apply px-0; + } + + .desktop-container { + @apply max-w-md mx-auto; + } + + .desktop-card { + @apply w-full mx-auto; + } + } } -.pb-safe-area { - padding-bottom: var(--safe-area-bottom); -} - -.ios-platform .needs-top-safe-area { - padding-top: var(--safe-area-top); -} - -.ios-platform .needs-bottom-safe-area { - padding-bottom: var(--safe-area-bottom); -} - -/* 추가 플랫폼별 스타일 */ -.ios-platform-only { - display: none; -} - -.android-platform-only { - display: none; -} - -.ios-platform .ios-platform-only { - display: block; -} - -.android-platform .android-platform-only { - display: block; +.font-inter { + font-family: 'Inter', sans-serif; } diff --git a/src/main.tsx b/src/main.tsx index 853d380..719464e 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,14 +1,5 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import App from './App' +import { createRoot } from 'react-dom/client' +import App from './App.tsx' import './index.css' -import { applySafeAreaInsets } from './utils/safeArea'; -// 안전 영역 CSS 변수 초기화 -applySafeAreaInsets(); - -ReactDOM.createRoot(document.getElementById('root')!).render( - - - , -) +createRoot(document.getElementById("root")!).render(); diff --git a/src/utils/safeArea.ts b/src/utils/safeArea.ts deleted file mode 100644 index 520bffd..0000000 --- a/src/utils/safeArea.ts +++ /dev/null @@ -1,61 +0,0 @@ - -import { isAndroidPlatform, isIOSPlatform } from './platform'; - -/** - * 기기 플랫폼에 따라 상단 안전 영역(Safe Area) 패딩 값을 반환합니다. - * iOS의 경우 다이나믹 아일랜드/노치를 위한 여백을 제공합니다. - */ -export const getTopSafeAreaPadding = (): string => { - if (isIOSPlatform()) { - return 'env(safe-area-inset-top, 47px)'; - } - return '0px'; -}; - -/** - * 기기 플랫폼에 따라 하단 안전 영역(Safe Area) 패딩 값을 반환합니다. - * iOS의 경우 홈 인디케이터를 위한 여백을 제공합니다. - */ -export const getBottomSafeAreaPadding = (): string => { - if (isIOSPlatform()) { - return 'env(safe-area-inset-bottom, 34px)'; - } - return '0px'; -}; - -/** - * 플랫폼 감지 및 안전 영역 CSS 변수를 문서에 적용합니다. - * 이 함수는 앱 초기화시 한 번 호출해야 합니다. - */ -export const applySafeAreaInsets = (): void => { - const root = document.documentElement; - - // 최초 로드 시 안전 영역 값 설정 - updateSafeAreaVariables(); - - // 방향 변경 시 안전 영역 값 다시 계산 - window.addEventListener('orientationchange', updateSafeAreaVariables); - window.addEventListener('resize', updateSafeAreaVariables); -}; - -/** - * 안전 영역 CSS 변수를 문서에 업데이트합니다. - */ -const updateSafeAreaVariables = (): void => { - const root = document.documentElement; - - // 플랫폼에 따른 안전 영역 패딩 계산 - root.style.setProperty('--safe-area-top', getTopSafeAreaPadding()); - root.style.setProperty('--safe-area-bottom', getBottomSafeAreaPadding()); - - // 디버깅용: 현재 사용 중인 플랫폼 표시 - if (isIOSPlatform()) { - root.classList.add('ios-platform'); - root.classList.remove('android-platform'); - } else if (isAndroidPlatform()) { - root.classList.add('android-platform'); - root.classList.remove('ios-platform'); - } else { - root.classList.remove('ios-platform', 'android-platform'); - } -};