Add dark mode with system preference auto-detection
All user-facing components now support dark mode via prefers-color-scheme. - Dark backgrounds: gray-950/900/800 - Dark text: gray-100/200/300/400 - Orange brand colors adapt with darker tints - Glass effects work in both modes - Skeletons, cards, filters, bottom sheet all themed - Google Maps InfoWindow stays light (maps don't support dark) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -418,9 +418,9 @@ export default function Home() {
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="h-screen flex flex-col">
|
||||
<div className="h-screen flex flex-col bg-white dark:bg-gray-950">
|
||||
{/* ── Header row 1: Logo + User ── */}
|
||||
<header className="bg-white/80 backdrop-blur-md border-b shrink-0">
|
||||
<header className="bg-white/80 dark:bg-gray-900/80 backdrop-blur-md border-b dark:border-gray-800 shrink-0">
|
||||
<div className="px-5 py-3 flex items-center justify-between">
|
||||
<button onClick={handleReset} className="text-lg font-bold whitespace-nowrap">
|
||||
Tasteby
|
||||
@@ -440,7 +440,7 @@ export default function Home() {
|
||||
setSelected(null);
|
||||
setShowDetail(false);
|
||||
}}
|
||||
className="border rounded-lg px-3 py-1.5 text-sm text-gray-600"
|
||||
className="border dark:border-gray-700 rounded-lg px-3 py-1.5 text-sm text-gray-600 dark:text-gray-300 dark:bg-gray-800"
|
||||
>
|
||||
<option value="">📺 전체 채널</option>
|
||||
{channels.map((ch) => (
|
||||
@@ -452,7 +452,7 @@ export default function Home() {
|
||||
<select
|
||||
value={cuisineFilter}
|
||||
onChange={(e) => setCuisineFilter(e.target.value)}
|
||||
className="border rounded-lg px-3 py-1.5 text-sm text-gray-600"
|
||||
className="border dark:border-gray-700 rounded-lg px-3 py-1.5 text-sm text-gray-600 dark:text-gray-300 dark:bg-gray-800"
|
||||
>
|
||||
<option value="">🍽 전체 장르</option>
|
||||
{CUISINE_GROUPS.map((g) => (
|
||||
@@ -462,7 +462,7 @@ export default function Home() {
|
||||
<select
|
||||
value={priceFilter}
|
||||
onChange={(e) => setPriceFilter(e.target.value)}
|
||||
className="border rounded-lg px-3 py-1.5 text-sm text-gray-600"
|
||||
className="border dark:border-gray-700 rounded-lg px-3 py-1.5 text-sm text-gray-600 dark:text-gray-300 dark:bg-gray-800"
|
||||
>
|
||||
<option value="">💰 전체 가격</option>
|
||||
{PRICE_GROUPS.map((g) => (
|
||||
@@ -475,7 +475,7 @@ export default function Home() {
|
||||
<select
|
||||
value={countryFilter}
|
||||
onChange={(e) => handleCountryChange(e.target.value)}
|
||||
className="border rounded-lg px-3 py-1.5 text-sm text-gray-600"
|
||||
className="border dark:border-gray-700 rounded-lg px-3 py-1.5 text-sm text-gray-600 dark:text-gray-300 dark:bg-gray-800"
|
||||
>
|
||||
<option value="">🌍 전체 나라</option>
|
||||
{countries.map((c) => (
|
||||
@@ -486,7 +486,7 @@ export default function Home() {
|
||||
<select
|
||||
value={cityFilter}
|
||||
onChange={(e) => handleCityChange(e.target.value)}
|
||||
className="border rounded-lg px-3 py-1.5 text-sm text-gray-600"
|
||||
className="border dark:border-gray-700 rounded-lg px-3 py-1.5 text-sm text-gray-600 dark:text-gray-300 dark:bg-gray-800"
|
||||
>
|
||||
<option value="">🏙 전체 시/도</option>
|
||||
{cities.map((c) => (
|
||||
@@ -498,7 +498,7 @@ export default function Home() {
|
||||
<select
|
||||
value={districtFilter}
|
||||
onChange={(e) => handleDistrictChange(e.target.value)}
|
||||
className="border rounded-lg px-3 py-1.5 text-sm text-gray-600"
|
||||
className="border dark:border-gray-700 rounded-lg px-3 py-1.5 text-sm text-gray-600 dark:text-gray-300 dark:bg-gray-800"
|
||||
>
|
||||
<option value="">🏘 전체 구/군</option>
|
||||
{districts.map((d) => (
|
||||
@@ -506,13 +506,13 @@ export default function Home() {
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
<div className="w-px h-5 bg-gray-200" />
|
||||
<div className="w-px h-5 bg-gray-200 dark:bg-gray-700" />
|
||||
<button
|
||||
onClick={() => setBoundsFilterOn(!boundsFilterOn)}
|
||||
className={`px-3 py-1.5 text-sm border rounded-lg transition-colors ${
|
||||
boundsFilterOn
|
||||
? "bg-orange-50 border-orange-300 text-orange-600"
|
||||
: "hover:bg-gray-100 text-gray-600"
|
||||
? "bg-orange-50 dark:bg-orange-900/30 border-orange-300 dark:border-orange-700 text-orange-600 dark:text-orange-400"
|
||||
: "hover:bg-gray-100 dark:hover:bg-gray-800 text-gray-600 dark:text-gray-400"
|
||||
}`}
|
||||
title="지도 영역 내 식당만 표시"
|
||||
>
|
||||
@@ -520,7 +520,7 @@ export default function Home() {
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setViewMode(viewMode === "map" ? "list" : "map")}
|
||||
className="px-3 py-1.5 text-sm border rounded-lg transition-colors hover:bg-gray-100 text-gray-600"
|
||||
className="px-3 py-1.5 text-sm border rounded-lg transition-colors hover:bg-gray-100 dark:hover:bg-gray-800 text-gray-600 dark:text-gray-400"
|
||||
title={viewMode === "map" ? "리스트 우선" : "지도 우선"}
|
||||
>
|
||||
{viewMode === "map" ? "🗺 지도" : "☰ 리스트"}
|
||||
@@ -531,8 +531,8 @@ export default function Home() {
|
||||
onClick={handleToggleFavorites}
|
||||
className={`px-3.5 py-1.5 text-sm rounded-full border transition-colors ${
|
||||
showFavorites
|
||||
? "bg-rose-50 border-rose-300 text-rose-600"
|
||||
: "border-gray-300 text-gray-600 hover:bg-gray-100"
|
||||
? "bg-rose-50 dark:bg-rose-900/30 border-rose-300 dark:border-rose-700 text-rose-600 dark:text-rose-400"
|
||||
: "border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-100"
|
||||
}`}
|
||||
>
|
||||
{showFavorites ? "♥ 내 찜" : "♡ 찜"}
|
||||
@@ -541,8 +541,8 @@ export default function Home() {
|
||||
onClick={handleToggleMyReviews}
|
||||
className={`px-3.5 py-1.5 text-sm rounded-full border transition-colors ${
|
||||
showMyReviews
|
||||
? "bg-orange-50 border-orange-300 text-orange-600"
|
||||
: "border-gray-300 text-gray-600 hover:bg-gray-100"
|
||||
? "bg-orange-50 dark:bg-orange-900/30 border-orange-300 dark:border-orange-700 text-orange-600 dark:text-orange-400"
|
||||
: "border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-100"
|
||||
}`}
|
||||
>
|
||||
{showMyReviews ? "✎ 내 리뷰" : "✎ 리뷰"}
|
||||
@@ -570,12 +570,12 @@ export default function Home() {
|
||||
{(user.nickname || user.email || "?").charAt(0).toUpperCase()}
|
||||
</div>
|
||||
)}
|
||||
<span className="hidden sm:inline text-sm font-medium text-gray-700">
|
||||
<span className="hidden sm:inline text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{user.nickname || user.email}
|
||||
</span>
|
||||
<button
|
||||
onClick={logout}
|
||||
className="ml-1 px-2.5 py-1 text-xs text-gray-500 border border-gray-300 rounded-full hover:bg-gray-100 hover:text-gray-700 transition-colors"
|
||||
className="ml-1 px-2.5 py-1 text-xs text-gray-500 dark:text-gray-400 border border-gray-300 dark:border-gray-700 rounded-full hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-200 transition-colors"
|
||||
>
|
||||
로그아웃
|
||||
</button>
|
||||
@@ -604,7 +604,7 @@ export default function Home() {
|
||||
onClick={() => setViewMode(viewMode === "map" ? "list" : "map")}
|
||||
className={`px-3 py-1.5 text-xs border rounded-lg transition-colors ${
|
||||
viewMode === "map"
|
||||
? "bg-orange-50 border-orange-300 text-orange-600"
|
||||
? "bg-orange-50 dark:bg-orange-900/30 border-orange-300 dark:border-orange-700 text-orange-600 dark:text-orange-400"
|
||||
: "text-gray-600"
|
||||
}`}
|
||||
>
|
||||
@@ -614,7 +614,7 @@ export default function Home() {
|
||||
onClick={() => setShowMobileFilters(!showMobileFilters)}
|
||||
className={`px-3 py-1.5 text-xs border rounded-lg transition-colors relative ${
|
||||
showMobileFilters || channelFilter || cuisineFilter || priceFilter || countryFilter || boundsFilterOn
|
||||
? "bg-orange-50 border-orange-300 text-orange-600"
|
||||
? "bg-orange-50 dark:bg-orange-900/30 border-orange-300 dark:border-orange-700 text-orange-600 dark:text-orange-400"
|
||||
: "text-gray-600"
|
||||
}`}
|
||||
>
|
||||
@@ -631,8 +631,8 @@ export default function Home() {
|
||||
onClick={handleToggleFavorites}
|
||||
className={`px-3 py-1.5 text-xs rounded-full border transition-colors ${
|
||||
showFavorites
|
||||
? "bg-rose-50 border-rose-300 text-rose-600"
|
||||
: "border-gray-300 text-gray-600"
|
||||
? "bg-rose-50 dark:bg-rose-900/30 border-rose-300 dark:border-rose-700 text-rose-600 dark:text-rose-400"
|
||||
: "border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-400"
|
||||
}`}
|
||||
>
|
||||
{showFavorites ? "♥ 찜" : "♡ 찜"}
|
||||
@@ -641,8 +641,8 @@ export default function Home() {
|
||||
onClick={handleToggleMyReviews}
|
||||
className={`px-3 py-1.5 text-xs rounded-full border transition-colors ${
|
||||
showMyReviews
|
||||
? "bg-orange-50 border-orange-300 text-orange-600"
|
||||
: "border-gray-300 text-gray-600"
|
||||
? "bg-orange-50 dark:bg-orange-900/30 border-orange-300 dark:border-orange-700 text-orange-600 dark:text-orange-400"
|
||||
: "border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-400"
|
||||
}`}
|
||||
>
|
||||
{showMyReviews ? "✎ 리뷰" : "✎ 리뷰"}
|
||||
@@ -656,7 +656,7 @@ export default function Home() {
|
||||
|
||||
{/* Collapsible filter panel */}
|
||||
{showMobileFilters && (
|
||||
<div className="bg-white/70 backdrop-blur-md rounded-xl p-3.5 space-y-3 border border-white/50 shadow-sm">
|
||||
<div className="bg-white/70 dark:bg-gray-900/70 backdrop-blur-md rounded-xl p-3.5 space-y-3 border border-white/50 dark:border-gray-700/50 shadow-sm">
|
||||
{/* Dropdown filters */}
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<select
|
||||
@@ -666,7 +666,7 @@ export default function Home() {
|
||||
setSelected(null);
|
||||
setShowDetail(false);
|
||||
}}
|
||||
className="border rounded-lg px-2.5 py-1.5 text-xs text-gray-600 bg-white"
|
||||
className="border dark:border-gray-700 rounded-lg px-2.5 py-1.5 text-xs text-gray-600 dark:text-gray-300 bg-white dark:bg-gray-800"
|
||||
>
|
||||
<option value="">📺 전체 채널</option>
|
||||
{channels.map((ch) => (
|
||||
@@ -678,7 +678,7 @@ export default function Home() {
|
||||
<select
|
||||
value={cuisineFilter}
|
||||
onChange={(e) => setCuisineFilter(e.target.value)}
|
||||
className="border rounded-lg px-2.5 py-1.5 text-xs text-gray-600 bg-white"
|
||||
className="border dark:border-gray-700 rounded-lg px-2.5 py-1.5 text-xs text-gray-600 dark:text-gray-300 bg-white dark:bg-gray-800"
|
||||
>
|
||||
<option value="">🍽 전체 장르</option>
|
||||
{CUISINE_GROUPS.map((g) => (
|
||||
@@ -688,7 +688,7 @@ export default function Home() {
|
||||
<select
|
||||
value={priceFilter}
|
||||
onChange={(e) => setPriceFilter(e.target.value)}
|
||||
className="border rounded-lg px-2.5 py-1.5 text-xs text-gray-600 bg-white"
|
||||
className="border dark:border-gray-700 rounded-lg px-2.5 py-1.5 text-xs text-gray-600 dark:text-gray-300 bg-white dark:bg-gray-800"
|
||||
>
|
||||
<option value="">💰 전체 가격</option>
|
||||
{PRICE_GROUPS.map((g) => (
|
||||
@@ -701,7 +701,7 @@ export default function Home() {
|
||||
<select
|
||||
value={countryFilter}
|
||||
onChange={(e) => handleCountryChange(e.target.value)}
|
||||
className="border rounded-lg px-2.5 py-1.5 text-xs text-gray-600 bg-white"
|
||||
className="border dark:border-gray-700 rounded-lg px-2.5 py-1.5 text-xs text-gray-600 dark:text-gray-300 bg-white dark:bg-gray-800"
|
||||
>
|
||||
<option value="">🌍 전체 나라</option>
|
||||
{countries.map((c) => (
|
||||
@@ -712,7 +712,7 @@ export default function Home() {
|
||||
<select
|
||||
value={cityFilter}
|
||||
onChange={(e) => handleCityChange(e.target.value)}
|
||||
className="border rounded-lg px-2.5 py-1.5 text-xs text-gray-600 bg-white"
|
||||
className="border dark:border-gray-700 rounded-lg px-2.5 py-1.5 text-xs text-gray-600 dark:text-gray-300 bg-white dark:bg-gray-800"
|
||||
>
|
||||
<option value="">🏙 전체 시/도</option>
|
||||
{cities.map((c) => (
|
||||
@@ -724,7 +724,7 @@ export default function Home() {
|
||||
<select
|
||||
value={districtFilter}
|
||||
onChange={(e) => handleDistrictChange(e.target.value)}
|
||||
className="border rounded-lg px-2.5 py-1.5 text-xs text-gray-600 bg-white"
|
||||
className="border dark:border-gray-700 rounded-lg px-2.5 py-1.5 text-xs text-gray-600 dark:text-gray-300 bg-white dark:bg-gray-800"
|
||||
>
|
||||
<option value="">🏘 전체 구/군</option>
|
||||
{districts.map((d) => (
|
||||
@@ -739,8 +739,8 @@ export default function Home() {
|
||||
onClick={() => setBoundsFilterOn(!boundsFilterOn)}
|
||||
className={`px-2.5 py-1.5 text-xs border rounded-lg transition-colors ${
|
||||
boundsFilterOn
|
||||
? "bg-orange-50 border-orange-300 text-orange-600"
|
||||
: "text-gray-600 bg-white"
|
||||
? "bg-orange-50 dark:bg-orange-900/30 border-orange-300 dark:border-orange-700 text-orange-600 dark:text-orange-400"
|
||||
: "text-gray-600 dark:text-gray-400 bg-white dark:bg-gray-800"
|
||||
}`}
|
||||
>
|
||||
{boundsFilterOn ? "📍 영역 ON" : "📍 영역"}
|
||||
@@ -757,7 +757,7 @@ export default function Home() {
|
||||
<div className="hidden md:flex flex-1 overflow-hidden">
|
||||
{viewMode === "map" ? (
|
||||
<>
|
||||
<aside className="w-80 bg-white border-r overflow-y-auto shrink-0">
|
||||
<aside className="w-80 bg-white dark:bg-gray-950 border-r dark:border-gray-800 overflow-y-auto shrink-0">
|
||||
{sidebarContent}
|
||||
</aside>
|
||||
<main className="flex-1 relative">
|
||||
@@ -777,10 +777,10 @@ export default function Home() {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<aside className="flex-1 bg-white overflow-y-auto">
|
||||
<aside className="flex-1 bg-white dark:bg-gray-950 overflow-y-auto">
|
||||
{sidebarContent}
|
||||
</aside>
|
||||
<main className="w-[40%] shrink-0 relative border-l">
|
||||
<main className="w-[40%] shrink-0 relative border-l dark:border-gray-800">
|
||||
<MapView
|
||||
restaurants={filteredRestaurants}
|
||||
selected={selected}
|
||||
@@ -819,7 +819,7 @@ export default function Home() {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex-1 bg-white overflow-y-auto">
|
||||
<div className="flex-1 bg-white dark:bg-gray-950 overflow-y-auto">
|
||||
{mobileListContent}
|
||||
{/* Scroll-down hint to reveal map */}
|
||||
<div className="flex flex-col items-center py-4 text-gray-300">
|
||||
@@ -827,7 +827,7 @@ export default function Home() {
|
||||
<span className="text-[10px]">아래로 스크롤하면 지도</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-[35vh] shrink-0 relative border-t">
|
||||
<div className="h-[35vh] shrink-0 relative border-t dark:border-gray-800">
|
||||
<MapView
|
||||
restaurants={filteredRestaurants}
|
||||
selected={selected}
|
||||
@@ -853,7 +853,7 @@ export default function Home() {
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="shrink-0 border-t bg-white/60 backdrop-blur-sm py-2.5 flex items-center justify-center gap-2 text-[11px] text-gray-400 group">
|
||||
<footer className="shrink-0 border-t dark:border-gray-800 bg-white/60 dark:bg-gray-900/60 backdrop-blur-sm py-2.5 flex items-center justify-center gap-2 text-[11px] text-gray-400 dark:text-gray-500 group">
|
||||
<div className="relative">
|
||||
<img
|
||||
src="/icon.jpg"
|
||||
|
||||
Reference in New Issue
Block a user