Saffron 디자인 시스템 적용: 브랜드 컬러 + Pretendard 폰트 + 크림 배경

- CSS 변수 기반 brand-50~950 컬러 팔레트 추가 (Tailwind @theme inline)
- Pretendard Variable 폰트 로드 및 기본 폰트로 설정
- 라이트모드 배경 #FFFAF5 크림색 적용 (다크모드 기본 유지)
- 전체 컴포넌트 orange-* → brand-* 마이그레이션
- 식당 리스트 채널명에 YouTube SVG 아이콘 추가
- 디자인 컨셉 문서 추가 (docs/design-concepts.md)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
joungmin
2026-03-11 21:15:45 +09:00
parent ec8330a978
commit 50018c17fa
12 changed files with 177 additions and 60 deletions

View File

@@ -808,7 +808,7 @@ function VideosPanel({ isAdmin }: { isAdmin: boolean }) {
<button
onClick={() => startBulkStream("transcript")}
disabled={bulkTranscripting || bulkExtracting}
className="bg-orange-600 text-white px-4 py-2 rounded text-sm hover:bg-orange-700 disabled:opacity-50"
className="bg-brand-600 text-white px-4 py-2 rounded text-sm hover:bg-brand-700 disabled:opacity-50"
>
{bulkTranscripting ? "자막 수집 중..." : "벌크 자막 수집"}
</button>
@@ -836,7 +836,7 @@ function VideosPanel({ isAdmin }: { isAdmin: boolean }) {
<button
onClick={startRemapFoods}
disabled={remappingFoods || bulkExtracting || bulkTranscripting || rebuildingVectors || remappingCuisine}
className="bg-orange-600 text-white px-4 py-2 rounded text-sm hover:bg-orange-700 disabled:opacity-50"
className="bg-brand-600 text-white px-4 py-2 rounded text-sm hover:bg-brand-700 disabled:opacity-50"
>
{remappingFoods ? "메뉴태그 재생성 중..." : "메뉴태그 재생성"}
</button>
@@ -849,7 +849,7 @@ function VideosPanel({ isAdmin }: { isAdmin: boolean }) {
<button
onClick={() => startBulkStream("transcript", Array.from(selected))}
disabled={bulkTranscripting || bulkExtracting}
className="bg-orange-500 text-white px-4 py-2 rounded text-sm hover:bg-orange-600 disabled:opacity-50"
className="bg-brand-500 text-white px-4 py-2 rounded text-sm hover:bg-brand-600 disabled:opacity-50"
>
({selected.size})
</button>
@@ -1073,7 +1073,7 @@ function VideosPanel({ isAdmin }: { isAdmin: boolean }) {
</h4>
<div className="w-full bg-gray-200 rounded-full h-2 mb-2">
<div
className="bg-orange-500 h-2 rounded-full transition-all"
className="bg-brand-500 h-2 rounded-full transition-all"
style={{ width: `${foodsProgress.total ? (foodsProgress.current / foodsProgress.total) * 100 : 0}%` }}
/>
</div>
@@ -1515,7 +1515,7 @@ function VideosPanel({ isAdmin }: { isAdmin: boolean }) {
{r.foods_mentioned.length > 0 && (
<div className="flex flex-wrap gap-1 mt-2">
{r.foods_mentioned.map((f, j) => (
<span key={j} className="px-1.5 py-0.5 bg-orange-50 text-orange-700 rounded text-xs">{f}</span>
<span key={j} className="px-1.5 py-0.5 bg-brand-50 text-brand-700 rounded text-xs">{f}</span>
))}
</div>
)}
@@ -1769,7 +1769,7 @@ function RestaurantsPanel({ isAdmin }: { isAdmin: boolean }) {
finally { setBulkTabling(false); load(); }
}}
disabled={bulkTabling}
className="px-3 py-1.5 text-xs bg-orange-500 text-white rounded hover:bg-orange-600 disabled:opacity-50"
className="px-3 py-1.5 text-xs bg-brand-500 text-white rounded hover:bg-brand-600 disabled:opacity-50"
>
{bulkTabling ? `테이블링 검색 중 (${bulkTablingProgress.current}/${bulkTablingProgress.total})` : "벌크 테이블링 연결"}
</button>
@@ -1825,13 +1825,13 @@ function RestaurantsPanel({ isAdmin }: { isAdmin: boolean }) {
</span>
</div>
{bulkTabling && bulkTablingProgress.name && (
<div className="bg-orange-50 rounded p-3 mb-4 text-sm">
<div className="bg-brand-50 rounded p-3 mb-4 text-sm">
<div className="flex justify-between mb-1">
<span>{bulkTablingProgress.current}/{bulkTablingProgress.total} - {bulkTablingProgress.name}</span>
<span className="text-xs text-gray-500">: {bulkTablingProgress.linked} / : {bulkTablingProgress.notFound}</span>
</div>
<div className="w-full bg-orange-200 rounded-full h-1.5">
<div className="bg-orange-500 h-1.5 rounded-full transition-all" style={{ width: `${(bulkTablingProgress.current / bulkTablingProgress.total) * 100}%` }} />
<div className="w-full bg-brand-200 rounded-full h-1.5">
<div className="bg-brand-500 h-1.5 rounded-full transition-all" style={{ width: `${(bulkTablingProgress.current / bulkTablingProgress.total) * 100}%` }} />
</div>
</div>
)}
@@ -1987,7 +1987,7 @@ function RestaurantsPanel({ isAdmin }: { isAdmin: boolean }) {
finally { setTablingSearching(false); }
}}
disabled={tablingSearching}
className="px-2 py-0.5 text-[11px] bg-orange-500 text-white rounded hover:bg-orange-600 disabled:opacity-50"
className="px-2 py-0.5 text-[11px] bg-brand-500 text-white rounded hover:bg-brand-600 disabled:opacity-50"
>
{tablingSearching ? "검색 중..." : "테이블링 검색"}
</button>

View File

@@ -1,15 +1,37 @@
@import "tailwindcss";
:root {
--background: #ffffff;
--background: #FFFAF5;
--foreground: #171717;
--brand-50: #FFF8F0;
--brand-100: #FFEDD5;
--brand-200: #FFD6A5;
--brand-300: #FFBC72;
--brand-400: #F5A623;
--brand-500: #F59E3F;
--brand-600: #E8720C;
--brand-700: #C45A00;
--brand-800: #9A4500;
--brand-900: #6B3000;
--brand-950: #3D1A00;
color-scheme: light dark;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist);
--color-brand-50: var(--brand-50);
--color-brand-100: var(--brand-100);
--color-brand-200: var(--brand-200);
--color-brand-300: var(--brand-300);
--color-brand-400: var(--brand-400);
--color-brand-500: var(--brand-500);
--color-brand-600: var(--brand-600);
--color-brand-700: var(--brand-700);
--color-brand-800: var(--brand-800);
--color-brand-900: var(--brand-900);
--color-brand-950: var(--brand-950);
--font-sans: var(--font-pretendard), var(--font-geist), system-ui, sans-serif;
}
@media (prefers-color-scheme: dark) {

View File

@@ -1,5 +1,6 @@
import type { Metadata } from "next";
import { Geist } from "next/font/google";
import localFont from "next/font/local";
import "./globals.css";
import { Providers } from "./providers";
@@ -8,6 +9,14 @@ const geist = Geist({
subsets: ["latin"],
});
const pretendard = localFont({
src: [
{ path: "../fonts/PretendardVariable.woff2", style: "normal" },
],
variable: "--font-pretendard",
display: "swap",
});
export const metadata: Metadata = {
title: "Tasteby - YouTube Restaurant Map",
description: "YouTube food channel restaurant map service",
@@ -20,7 +29,7 @@ export default function RootLayout({
}>) {
return (
<html lang="ko" className="dark:bg-gray-950" suppressHydrationWarning>
<body className={`${geist.variable} font-sans antialiased`}>
<body className={`${pretendard.variable} ${geist.variable} font-sans antialiased`}>
<Providers>{children}</Providers>
</body>
</html>

View File

@@ -600,7 +600,7 @@ export default function Home() {
</div>
<button
onClick={handleReset}
className="p-1.5 text-gray-400 dark:text-gray-500 hover:text-orange-500 dark:hover:text-orange-400 transition-colors"
className="p-1.5 text-gray-400 dark:text-gray-500 hover:text-brand-500 dark:hover:text-brand-400 transition-colors"
title="초기화"
>
<svg viewBox="0 0 24 24" className="w-4.5 h-4.5 fill-current"><path d="M17.65 6.35A7.958 7.958 0 0012 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0112 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>
@@ -635,8 +635,8 @@ export default function Home() {
onClick={handleToggleMyReviews}
className={`px-2.5 py-1 text-xs rounded-lg border transition-colors ${
showMyReviews
? "bg-orange-50 dark:bg-orange-900/30 border-orange-300 dark:border-orange-700 text-orange-600 dark:text-orange-400"
: "border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 hover:border-orange-300 hover:text-orange-500"
? "bg-brand-50 dark:bg-brand-900/30 border-brand-300 dark:border-brand-700 text-brand-600 dark:text-brand-400"
: "border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 hover:border-brand-300 hover:text-brand-500"
}`}
>
{showMyReviews ? "✎ 내 리뷰" : "✎ 리뷰"}
@@ -650,7 +650,7 @@ export default function Home() {
{user.avatar_url ? (
<img src={user.avatar_url} alt="" className="w-7 h-7 rounded-full border border-gray-200" />
) : (
<div className="w-7 h-7 rounded-full bg-orange-100 text-orange-700 flex items-center justify-center text-xs font-semibold border border-orange-200">
<div className="w-7 h-7 rounded-full bg-brand-100 text-brand-700 flex items-center justify-center text-xs font-semibold border border-brand-200">
{(user.nickname || user.email || "?").charAt(0).toUpperCase()}
</div>
)}
@@ -684,15 +684,15 @@ export default function Home() {
}}
className={`shrink-0 flex items-center gap-2 rounded-lg px-3 py-1.5 border transition-all text-left ${
channelFilter === ch.channel_name
? "bg-orange-50 dark:bg-orange-900/30 border-orange-300 dark:border-orange-700"
: "bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 hover:border-orange-200 dark:hover:border-orange-800"
? "bg-brand-50 dark:bg-brand-900/30 border-brand-300 dark:border-brand-700"
: "bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 hover:border-brand-200 dark:hover:border-brand-800"
}`}
style={{ width: "200px" }}
>
<svg className="w-4 h-4 shrink-0 text-red-500" viewBox="0 0 24 24" fill="currentColor"><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>
<div className="min-w-0 flex-1">
<p className={`text-xs font-semibold truncate ${
channelFilter === ch.channel_name ? "text-orange-600 dark:text-orange-400" : "dark:text-gray-200"
channelFilter === ch.channel_name ? "text-brand-600 dark:text-brand-400" : "dark:text-gray-200"
}`}>{ch.channel_name}</p>
{ch.description && <p className="text-[10px] text-gray-400 dark:text-gray-500 truncate">{ch.description}</p>}
</div>
@@ -710,7 +710,7 @@ export default function Home() {
value={cuisineFilter}
onChange={(e) => { setCuisineFilter(e.target.value); if (e.target.value) setBoundsFilterOn(false); }}
className={`bg-transparent border-none outline-none cursor-pointer pr-1 ${
cuisineFilter ? "text-orange-600 dark:text-orange-400 font-medium" : "text-gray-500 dark:text-gray-400"
cuisineFilter ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"
}`}
>
<option value=""></option>
@@ -730,7 +730,7 @@ export default function Home() {
value={priceFilter}
onChange={(e) => { setPriceFilter(e.target.value); if (e.target.value) setBoundsFilterOn(false); }}
className={`bg-transparent border-none outline-none cursor-pointer pr-1 ${
priceFilter ? "text-orange-600 dark:text-orange-400 font-medium" : "text-gray-500 dark:text-gray-400"
priceFilter ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"
}`}
>
<option value=""></option>
@@ -741,7 +741,7 @@ export default function Home() {
{(cuisineFilter || priceFilter) && (
<button
onClick={() => { setCuisineFilter(""); setPriceFilter(""); }}
className="text-gray-400 hover:text-orange-500 transition-colors"
className="text-gray-400 hover:text-brand-500 transition-colors"
title="음식 필터 초기화"
>
<svg viewBox="0 0 24 24" className="w-3 h-3 fill-current"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
@@ -755,7 +755,7 @@ export default function Home() {
value={countryFilter}
onChange={(e) => handleCountryChange(e.target.value)}
className={`bg-transparent border-none outline-none cursor-pointer pr-1 ${
countryFilter ? "text-orange-600 dark:text-orange-400 font-medium" : "text-gray-500 dark:text-gray-400"
countryFilter ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"
}`}
>
<option value=""></option>
@@ -770,7 +770,7 @@ export default function Home() {
value={cityFilter}
onChange={(e) => handleCityChange(e.target.value)}
className={`bg-transparent border-none outline-none cursor-pointer pr-1 ${
cityFilter ? "text-orange-600 dark:text-orange-400 font-medium" : "text-gray-500 dark:text-gray-400"
cityFilter ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"
}`}
>
<option value="">/</option>
@@ -787,7 +787,7 @@ export default function Home() {
value={districtFilter}
onChange={(e) => handleDistrictChange(e.target.value)}
className={`bg-transparent border-none outline-none cursor-pointer pr-1 ${
districtFilter ? "text-orange-600 dark:text-orange-400 font-medium" : "text-gray-500 dark:text-gray-400"
districtFilter ? "text-brand-600 dark:text-brand-400 font-medium" : "text-gray-500 dark:text-gray-400"
}`}
>
<option value="">/</option>
@@ -800,7 +800,7 @@ export default function Home() {
{countryFilter && (
<button
onClick={() => { setCountryFilter(""); setCityFilter(""); setDistrictFilter(""); setRegionFlyTo(null); }}
className="text-gray-400 hover:text-orange-500 transition-colors"
className="text-gray-400 hover:text-brand-500 transition-colors"
title="지역 필터 초기화"
>
<svg viewBox="0 0 24 24" className="w-3 h-3 fill-current"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
@@ -819,7 +819,7 @@ export default function Home() {
setDistrictFilter("");
setRegionFlyTo(null);
}}
className="flex items-center gap-1 rounded-lg px-2 py-1 bg-gray-50 dark:bg-gray-800/50 text-gray-500 dark:text-gray-400 hover:text-orange-500 transition-colors"
className="flex items-center gap-1 rounded-lg px-2 py-1 bg-gray-50 dark:bg-gray-800/50 text-gray-500 dark:text-gray-400 hover:text-brand-500 transition-colors"
>
<svg viewBox="0 0 24 24" className="w-3 h-3 fill-current"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
<span></span>
@@ -850,8 +850,8 @@ export default function Home() {
}}
className={`flex items-center gap-1 rounded-lg px-2 py-1 transition-colors ${
boundsFilterOn
? "bg-orange-50 dark:bg-orange-900/30 text-orange-600 dark:text-orange-400"
: "bg-gray-50 dark:bg-gray-800/50 text-gray-500 dark:text-gray-400 hover:text-orange-500"
? "bg-brand-50 dark:bg-brand-900/30 text-brand-600 dark:text-brand-400"
: "bg-gray-50 dark:bg-gray-800/50 text-gray-500 dark:text-gray-400 hover:text-brand-500"
}`}
title="내 위치 주변 식당만 표시"
>
@@ -879,7 +879,7 @@ export default function Home() {
onClick={handleToggleMyReviews}
className={`px-2 py-0.5 text-[10px] rounded-full border transition-colors ${
showMyReviews
? "bg-orange-50 dark:bg-orange-900/30 border-orange-300 dark:border-orange-700 text-orange-600 dark:text-orange-400"
? "bg-brand-50 dark:bg-brand-900/30 border-brand-300 dark:border-brand-700 text-brand-600 dark:text-brand-400"
: "border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400"
}`}
>
@@ -892,7 +892,7 @@ export default function Home() {
{user.avatar_url ? (
<img src={user.avatar_url} alt="" className="w-7 h-7 rounded-full border border-gray-200" />
) : (
<div className="w-7 h-7 rounded-full bg-orange-100 text-orange-700 flex items-center justify-center text-xs font-semibold border border-orange-200">
<div className="w-7 h-7 rounded-full bg-brand-100 text-brand-700 flex items-center justify-center text-xs font-semibold border border-brand-200">
{(user.nickname || user.email || "?").charAt(0).toUpperCase()}
</div>
)}
@@ -920,7 +920,7 @@ export default function Home() {
}}
className={`shrink-0 rounded-xl px-3 py-2 text-left border transition-all ${
channelFilter === ch.channel_name
? "bg-orange-50 dark:bg-orange-900/30 border-orange-300 dark:border-orange-700"
? "bg-brand-50 dark:bg-brand-900/30 border-brand-300 dark:border-brand-700"
: "bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700"
}`}
style={{ minWidth: "140px", maxWidth: "170px" }}
@@ -928,7 +928,7 @@ export default function Home() {
<div className="flex items-center gap-1.5">
<svg className="w-3.5 h-3.5 shrink-0 text-red-500" viewBox="0 0 24 24" fill="currentColor"><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>
<p className={`text-xs font-semibold truncate ${
channelFilter === ch.channel_name ? "text-orange-600 dark:text-orange-400" : "dark:text-gray-200"
channelFilter === ch.channel_name ? "text-brand-600 dark:text-brand-400" : "dark:text-gray-200"
}`}>{ch.channel_name}</p>
</div>
{ch.description && <p className="text-[10px] text-gray-500 dark:text-gray-400 truncate mt-0.5">{ch.description}</p>}
@@ -952,7 +952,7 @@ export default function Home() {
value={cuisineFilter}
onChange={(e) => { setCuisineFilter(e.target.value); if (e.target.value) setBoundsFilterOn(false); }}
className={`border dark:border-gray-700 rounded-lg px-2 py-1 bg-white dark:bg-gray-800 ${
cuisineFilter ? "text-orange-600 dark:text-orange-400 border-orange-300 dark:border-orange-700" : "text-gray-500 dark:text-gray-400"
cuisineFilter ? "text-brand-600 dark:text-brand-400 border-brand-300 dark:border-brand-700" : "text-gray-500 dark:text-gray-400"
}`}
>
<option value="">🍽 </option>
@@ -971,7 +971,7 @@ export default function Home() {
value={priceFilter}
onChange={(e) => { setPriceFilter(e.target.value); if (e.target.value) setBoundsFilterOn(false); }}
className={`border dark:border-gray-700 rounded-lg px-2 py-1 bg-white dark:bg-gray-800 ${
priceFilter ? "text-orange-600 dark:text-orange-400 border-orange-300 dark:border-orange-700" : "text-gray-500 dark:text-gray-400"
priceFilter ? "text-brand-600 dark:text-brand-400 border-brand-300 dark:border-brand-700" : "text-gray-500 dark:text-gray-400"
}`}
>
<option value="">💰 </option>
@@ -980,7 +980,7 @@ export default function Home() {
))}
</select>
{(cuisineFilter || priceFilter) && (
<button onClick={() => { setCuisineFilter(""); setPriceFilter(""); }} className="text-gray-400 hover:text-orange-500">
<button onClick={() => { setCuisineFilter(""); setPriceFilter(""); }} className="text-gray-400 hover:text-brand-500">
<svg viewBox="0 0 24 24" className="w-3.5 h-3.5 fill-current"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</button>
)}
@@ -992,7 +992,7 @@ export default function Home() {
value={countryFilter}
onChange={(e) => handleCountryChange(e.target.value)}
className={`border dark:border-gray-700 rounded-lg px-2 py-1 bg-white dark:bg-gray-800 ${
countryFilter ? "text-orange-600 dark:text-orange-400 border-orange-300 dark:border-orange-700" : "text-gray-500 dark:text-gray-400"
countryFilter ? "text-brand-600 dark:text-brand-400 border-brand-300 dark:border-brand-700" : "text-gray-500 dark:text-gray-400"
}`}
>
<option value="">🌍 </option>
@@ -1005,7 +1005,7 @@ export default function Home() {
value={cityFilter}
onChange={(e) => handleCityChange(e.target.value)}
className={`border dark:border-gray-700 rounded-lg px-2 py-1 bg-white dark:bg-gray-800 ${
cityFilter ? "text-orange-600 dark:text-orange-400 border-orange-300 dark:border-orange-700" : "text-gray-500 dark:text-gray-400"
cityFilter ? "text-brand-600 dark:text-brand-400 border-brand-300 dark:border-brand-700" : "text-gray-500 dark:text-gray-400"
}`}
>
<option value="">/</option>
@@ -1019,7 +1019,7 @@ export default function Home() {
value={districtFilter}
onChange={(e) => handleDistrictChange(e.target.value)}
className={`border dark:border-gray-700 rounded-lg px-2 py-1 bg-white dark:bg-gray-800 ${
districtFilter ? "text-orange-600 dark:text-orange-400 border-orange-300 dark:border-orange-700" : "text-gray-500 dark:text-gray-400"
districtFilter ? "text-brand-600 dark:text-brand-400 border-brand-300 dark:border-brand-700" : "text-gray-500 dark:text-gray-400"
}`}
>
<option value="">/</option>
@@ -1029,7 +1029,7 @@ export default function Home() {
</select>
)}
{countryFilter && (
<button onClick={() => { setCountryFilter(""); setCityFilter(""); setDistrictFilter(""); setRegionFlyTo(null); }} className="text-gray-400 hover:text-orange-500">
<button onClick={() => { setCountryFilter(""); setCityFilter(""); setDistrictFilter(""); setRegionFlyTo(null); }} className="text-gray-400 hover:text-brand-500">
<svg viewBox="0 0 24 24" className="w-3.5 h-3.5 fill-current"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</button>
)}
@@ -1056,7 +1056,7 @@ export default function Home() {
}}
className={`flex items-center gap-0.5 rounded-lg px-2 py-1 border transition-colors ${
boundsFilterOn
? "bg-orange-50 dark:bg-orange-900/30 border-orange-300 dark:border-orange-700 text-orange-600 dark:text-orange-400"
? "bg-brand-50 dark:bg-brand-900/30 border-brand-300 dark:border-brand-700 text-brand-600 dark:text-brand-400"
: "border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400"
}`}
>
@@ -1135,7 +1135,7 @@ export default function Home() {
activeChannel={channelFilter || undefined}
/>
<div className="absolute top-2 left-2 bg-white/90 dark:bg-gray-900/90 backdrop-blur-sm rounded-lg px-3 py-1.5 shadow-sm z-10">
<span className="text-xs font-medium text-orange-600 dark:text-orange-400">
<span className="text-xs font-medium text-brand-600 dark:text-brand-400">
{filteredRestaurants.length}
</span>
</div>
@@ -1167,7 +1167,7 @@ export default function Home() {
{user.avatar_url ? (
<img src={user.avatar_url} alt="" className="w-12 h-12 rounded-full border border-gray-200" />
) : (
<div className="w-12 h-12 rounded-full bg-orange-100 text-orange-700 flex items-center justify-center text-lg font-semibold border border-orange-200">
<div className="w-12 h-12 rounded-full bg-brand-100 text-brand-700 flex items-center justify-center text-lg font-semibold border border-brand-200">
{(user.nickname || user.email || "?").charAt(0).toUpperCase()}
</div>
)}
@@ -1259,7 +1259,7 @@ export default function Home() {
onClick={() => handleMobileTab(tab.key)}
className={`flex-1 flex flex-col items-center justify-center gap-0.5 py-2 transition-colors ${
mobileTab === tab.key
? "text-orange-600 dark:text-orange-400"
? "text-brand-600 dark:text-brand-400"
: "text-gray-400 dark:text-gray-500"
}`}
>
@@ -1276,10 +1276,10 @@ export default function Home() {
<img
src="/icon.jpg"
alt="SDJ Labs"
className="w-6 h-6 rounded-full border-2 border-orange-200 shadow-sm group-hover:scale-110 group-hover:rotate-12 transition-all duration-300"
className="w-6 h-6 rounded-full border-2 border-brand-200 shadow-sm group-hover:scale-110 group-hover:rotate-12 transition-all duration-300"
/>
<span className="absolute -top-0.5 -right-0.5 w-2 h-2 bg-orange-300 rounded-full animate-ping opacity-75" />
<span className="absolute -top-0.5 -right-0.5 w-2 h-2 bg-orange-400 rounded-full" />
<span className="absolute -top-0.5 -right-0.5 w-2 h-2 bg-brand-300 rounded-full animate-ping opacity-75" />
<span className="absolute -top-0.5 -right-0.5 w-2 h-2 bg-brand-400 rounded-full" />
</div>
<span className="font-medium tracking-wide group-hover:text-gray-600 transition-colors">
SDJ Labs Co., Ltd.

View File

@@ -15,7 +15,7 @@ export default function LoginMenu({ onGoogleSuccess }: LoginMenuProps) {
<>
<button
onClick={() => setOpen(true)}
className="px-3 py-1.5 text-sm font-medium text-gray-600 dark:text-gray-300 hover:text-orange-600 dark:hover:text-orange-400 border border-gray-300 dark:border-gray-600 hover:border-orange-400 dark:hover:border-orange-500 rounded-lg transition-colors"
className="px-3 py-1.5 text-sm font-medium text-gray-600 dark:text-gray-300 hover:text-brand-600 dark:hover:text-brand-400 border border-gray-300 dark:border-gray-600 hover:border-brand-400 dark:hover:border-brand-500 rounded-lg transition-colors"
>
</button>

View File

@@ -231,7 +231,7 @@ export default function MapView({ restaurants, selected, onSelectRestaurant, onB
{onMyLocation && (
<button
onClick={onMyLocation}
className="absolute top-2 right-2 w-9 h-9 bg-white dark:bg-gray-900 rounded-lg shadow-md flex items-center justify-center text-gray-600 dark:text-gray-300 hover:text-orange-500 dark:hover:text-orange-400 transition-colors z-10"
className="absolute top-2 right-2 w-9 h-9 bg-white dark:bg-gray-900 rounded-lg shadow-md flex items-center justify-center text-gray-600 dark:text-gray-300 hover:text-brand-500 dark:hover:text-brand-400 transition-colors z-10"
title="내 위치"
>
<svg viewBox="0 0 24 24" className="w-5 h-5 fill-current">

View File

@@ -117,7 +117,7 @@ export default function RestaurantDetail({
{restaurant.phone && (
<p>
<span className="text-gray-500 dark:text-gray-400">:</span>{" "}
<a href={`tel:${restaurant.phone}`} className="text-orange-600 dark:text-orange-400 hover:underline">
<a href={`tel:${restaurant.phone}`} className="text-brand-600 dark:text-brand-400 hover:underline">
{restaurant.phone}
</a>
</p>
@@ -128,7 +128,7 @@ export default function RestaurantDetail({
href={`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(restaurant.name + (restaurant.address ? " " + restaurant.address : restaurant.region ? " " + restaurant.region.replace(/\|/g, " ") : ""))}`}
target="_blank"
rel="noopener noreferrer"
className="text-orange-600 dark:text-orange-400 hover:underline text-xs"
className="text-brand-600 dark:text-brand-400 hover:underline text-xs"
>
Google Maps에서
</a>
@@ -223,7 +223,7 @@ export default function RestaurantDetail({
{v.foods_mentioned.map((f, i) => (
<span
key={i}
className="px-2 py-0.5 bg-orange-50 dark:bg-orange-900/30 text-orange-700 dark:text-orange-400 rounded text-xs"
className="px-2 py-0.5 bg-brand-50 dark:bg-brand-900/30 text-brand-700 dark:text-brand-400 rounded text-xs"
>
{f}
</span>

View File

@@ -39,7 +39,7 @@ export default function RestaurantList({
onClick={() => onSelect(r)}
className={`w-full text-left p-3 rounded-xl shadow-sm border transition-all hover:shadow-md hover:-translate-y-0.5 ${
selectedId === r.id
? "bg-orange-50 dark:bg-orange-900/20 border-orange-300 dark:border-orange-700 shadow-orange-100 dark:shadow-orange-900/10"
? "bg-brand-50 dark:bg-brand-900/20 border-brand-300 dark:border-brand-700 shadow-brand-100 dark:shadow-brand-900/10"
: "bg-white dark:bg-gray-900 border-gray-100 dark:border-gray-800 hover:bg-gray-50 dark:hover:bg-gray-800"
}`}
>
@@ -70,7 +70,7 @@ export default function RestaurantList({
{r.foods_mentioned.slice(0, 5).map((f, i) => (
<span
key={i}
className="px-1.5 py-0.5 bg-orange-50 dark:bg-orange-900/30 text-orange-700 dark:text-orange-400 rounded text-[10px]"
className="px-1.5 py-0.5 bg-brand-50 dark:bg-brand-900/30 text-brand-700 dark:text-brand-400 rounded text-[10px]"
>
{f}
</span>
@@ -85,8 +85,9 @@ export default function RestaurantList({
{r.channels.map((ch) => (
<span
key={ch}
className="px-1.5 py-0.5 bg-orange-50 dark:bg-orange-900/30 text-orange-600 dark:text-orange-400 rounded-full text-[10px] font-medium"
className="inline-flex items-center gap-0.5 px-1.5 py-0.5 bg-brand-50 dark:bg-brand-900/30 text-brand-600 dark:text-brand-400 rounded-full text-[10px] font-medium"
>
<svg className="w-2.5 h-2.5 shrink-0 text-red-400" viewBox="0 0 24 24" fill="currentColor"><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>
{ch}
</span>
))}

View File

@@ -124,7 +124,7 @@ function ReviewForm({
<button
type="submit"
disabled={submitting}
className="px-3 py-1 bg-orange-500 dark:bg-orange-600 text-white text-sm rounded hover:bg-orange-600 dark:hover:bg-orange-500 disabled:opacity-50"
className="px-3 py-1 bg-brand-500 dark:bg-brand-600 text-white text-sm rounded hover:bg-brand-600 dark:hover:bg-brand-500 disabled:opacity-50"
>
{submitting ? "저장 중..." : submitLabel}
</button>
@@ -225,7 +225,7 @@ export default function ReviewSection({ restaurantId }: ReviewSectionProps) {
{user && !myReview && !showForm && (
<button
onClick={() => setShowForm(true)}
className="mb-3 px-3 py-1 bg-orange-500 dark:bg-orange-600 text-white text-sm rounded hover:bg-orange-600 dark:hover:bg-orange-500"
className="mb-3 px-3 py-1 bg-brand-500 dark:bg-brand-600 text-white text-sm rounded hover:bg-brand-600 dark:hover:bg-brand-500"
>
</button>

View File

@@ -36,11 +36,11 @@ export default function SearchBar({ onSearch, isLoading }: SearchBarProps) {
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="식당, 지역, 음식 검색..."
className="w-full pl-9 pr-3 py-2 bg-gray-100 dark:bg-gray-800 border border-transparent focus:border-orange-400 focus:bg-white dark:focus:bg-gray-900 rounded-xl text-sm outline-none transition-all dark:text-gray-200 dark:placeholder-gray-500"
className="w-full pl-9 pr-3 py-2 bg-gray-100 dark:bg-gray-800 border border-transparent focus:border-brand-400 focus:bg-white dark:focus:bg-gray-900 rounded-xl text-sm outline-none transition-all dark:text-gray-200 dark:placeholder-gray-500"
/>
{isLoading && (
<div className="absolute right-3 top-1/2 -translate-y-1/2">
<div className="w-4 h-4 border-2 border-orange-400 border-t-transparent rounded-full animate-spin" />
<div className="w-4 h-4 border-2 border-brand-400 border-t-transparent rounded-full animate-spin" />
</div>
)}
</form>

Binary file not shown.