From 0a96babc98dcad191f2d9f7899f3273c5f2873c7 Mon Sep 17 00:00:00 2001 From: Haewon Kam Date: Tue, 9 Jun 2026 11:28:11 +0900 Subject: [PATCH] Add DESIGN.md (look-and-feel SSOT) + rewrite HANDOFF as next-session entry point - docs/DESIGN.md: exact color tokens, Impact typography, component catalog, assets, Do/Dont, pending 06.09 design changes - HANDOFF.md: accurate current state (single-match v2), correct file map, 4 SSOT read-order, confirmed decisions, prioritized next-session tasks (multi-match A / detail B / firebase C) Co-Authored-By: Claude Opus 4.8 --- HANDOFF.md | 123 ++++++++++++++++++++++--------------------------- docs/DESIGN.md | 100 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 67 deletions(-) create mode 100644 docs/DESIGN.md diff --git a/HANDOFF.md b/HANDOFF.md index 296edde..6ec1d2c 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -1,83 +1,72 @@ -# TriplePick Web — HANDOFF (다음 세션용) +# TriplePick Web — HANDOFF (다음 세션 진입점) -최종 업데이트: 2026-06-08 KST -작성: 프론트엔드 R1(mock) 완료 시점 +최종 갱신: 2026-06-09 KST +> 새 세션은 이전 대화 기억이 없다. **이 문서 → 아래 4개 SSOT 순서로 읽고 시작**하면 그대로 구현 가능. -> 이 문서는 **새 세션에서 이어받기** 위한 것. 새 세션은 이전 대화 기억이 없으니 여기 + 아래 기획 문서만 보고 작업한다. +## 📌 새 세션 시작 시 읽을 순서 +1. **이 파일** (현재 상태·파일맵·실행) +2. `docs/DESIGN.md` — **룩앤필/디자인 시스템 SSOT** (컬러·폰트·컴포넌트·자산·Do/Don't) +3. `../Worldcup 2026/000. Plan/Task — 랜딩·디자인·프론트엔드 업데이트 (06.09 회의).md` — **할 일 전체(우선순위·금요일 스코프)** +4. `docs/BACKEND.md` — 백엔드(Firestore·함수·채점·Resend·투표창) +5. (참고) `../Worldcup 2026/000. Plan/Landing 기능정의서 — TriplePick 2026.md` / `Landing PRD — TriplePick 2026.md` -## 0. 이 프로젝트가 뭔가 -2026 월드컵 **TriplePick** AI 승부예측 랜딩. 한국 vs 체코 단일 경기 R1. -사용자가 GPT·Claude·Gemini 예측을 보고 자기 픽을 제출 → 같은 선택 AI 확인 → 공유 → 100만원 챌린지. +## 0. 프로젝트 +TriplePick = "AI(GPT·Claude·Gemini) vs 너" 글로벌 축구 승부예측 랜딩. (※ "월드컵" 명칭 법적 사용불가) +픽 제출 → 같은 AI 확인 → 공유 → 포인트/100만원 챌린지. 모바일 우선. -기획 SSOT (상위 폴더): -- `../Worldcup 2026/000. Plan/Landing PRD — TriplePick 2026.md` -- `../Worldcup 2026/000. Plan/Landing 기능정의서 — TriplePick 2026.md` -- 디자인: `../Worldcup 2026/000. Plan/002. Landing Visuals/TriplePick2026 Profile image/` +## 1. 현재 상태 (DONE) +- Next.js 16 + React 19 + Tailwind v4 (App Router, TS). `npm run build` 통과. +- **단일 경기(한국 vs 체코) v2** 구현 — 디자인 002/003 + 사용자 디렉션 반영(Impact, 민트 #85FCA8, 틸그린 GPT, 클래식 축구공, 화이트 시트, 쿨톤 상금). +- 인터랙션: 픽 → (닉네임)이메일 → 제출 → 결과(같은 AI) → Crowd 증가 (전부 **mock**, 백엔드 미연결). +- 배포: https://triplepick-web.vercel.app (`/` = 현재, `/before` = v1 기록용). +- git: `fa68169` init → `5ba653c` /before → `1b9dbd2` Backend Pack → (투표창) 커밋들. -## 1. 현재 상태 (DONE — 프론트 R1, mock 데이터) -- Next.js 16 + React 19 + Tailwind v4 (App Router, TS) -- 모바일 우선, 다크 그린 스코어보드 톤 (디자인 002/003 정합) -- 구현 섹션: TopBar(카운트다운·참여수·CTA) / Hero / MatchCard / AIPredictions(3모델 confidence바) / Experience(픽·제출·결과·공유·Crowd) / PrizeChallenge / Footer(법무 4고지) -- 인터랙션 검증 완료: 픽 선택 → 닉네임 → 제출 → 결과(같은 AI 계산) → Crowd 실시간 증가 -- `npm run build` 통과 - -## 2. 실행 방법 +## 2. 실행 ```bash cd /Users/haewonkam/Claude/AIO2O/triplepick-web -npm install # 최초 1회 -npm run dev -- --port 3737 -# http://localhost:3737 +npm install +npm run dev -- --port 3737 # http://localhost:3737 ``` -## 3. 파일 맵 +## 3. 파일 맵 (현행) ``` -app/layout.tsx 메타데이터·OG·SEO (ko) -app/page.tsx Hero → MatchupHUD → Arena → Footer -app/globals.css 테마 토큰, .sheet(레이어드 화이트), .bracket(HUD), .btn-mint, .gold-* -lib/types.ts 도메인 타입 (Firestore 변환기에 재사용) -lib/mockData.ts ⚠️ MOCK — Firebase 연결 시 여기부터 교체 -lib/format.ts 라벨/시간/퍼센트 포맷 -components/Hero.tsx 헤더 + 후킹 (다크) -components/MatchupHUD.tsx Match01→Final + 코너브래킷 + 녹색글로우 VS (003 대결 강조) -components/Arena.tsx ⚠️ 레이어드 화이트 시트(AI예측+픽+결과) + 골드 상금 + Crowd (client) — submit이 mock -components/Footer.tsx 고지 (003 fine print + 법무) +app/layout.tsx 메타·OG·SEO(ko) +app/page.tsx Hero → MatchupHUD → Arena → Footer (단일 경기) +app/before/page.tsx /before (v1 기록용, 자체완결) +app/globals.css 디자인 토큰 (DESIGN.md와 1:1) +lib/types.ts 도메인 타입 (opensAt/lockAt 포함) +lib/schedule.ts Group A 6경기 일정(KST) SSOT — opensAt=킥오프-48h, lockAt=킥오프 +lib/mockData.ts ⚠️ MOCK AI예측/Crowd + DEMO_FORCE_OPEN 플래그 +lib/format.ts 시간·라벨 포맷 +lib/firebase.ts 클라 초기화 스켈레톤(env) +components/Hero.tsx · MatchupHUD.tsx · Arena.tsx(핵심) · BallIcon.tsx · Footer.tsx +public/icons/ ball.png(투명) profile.png gpt/claude/gemini kor.png +firestore.rules · firestore.indexes.json · firebase.json · .env.example · scripts/seed.mjs +docs/DESIGN.md · docs/BACKEND.md ``` -> 디자인 정합: 002(레이어드 화이트 백그라운드) + 003(매칭 대결 강조·Match01→Final·골드 ₩1,000,000·Crowd 가로바). 버튼 민트 #85FCA8(--mint). 모델 컬러 GPT #19c37d / Claude #e8814a / Gemini #5b8cff. -> 참고: 002/003에 없는 sticky 카운트다운 TopBar는 디자인 충실성을 위해 제거함(PRD의 카운트다운/마감 로직은 Firebase 단계에서 헤더 또는 픽 옆에 재도입 가능). 세그먼트 기본 선택 없음(픽 강제) — 목업은 한국 승 선택 상태로 표시되어 있으니 기본값 원하면 Arena의 useState(outcome) 기본값 지정. +## 4. 확정 결정 (구현에 반영됨/반영할 것) +- **투표창**: 오픈 = 킥오프 D-2(-48h, `opensAt`) / 마감 = **킥오프 정각**(`lockAt`, 경기 전까지). `DEMO_FORCE_OPEN=true`로 데모는 항상 열림 → **운영 시 false**. +- **AI 예측**: 매일 KST 0시 1회 생성, 그날 고정(immutable), 남은 경기만. (현재 mock) +- **결과 메일**: 경기 3시간 후, **맞춘 사람만** + 다음경기/100만원 링크 (Resend). +- **Firebase**: 사용자가 새 프로젝트 직접 생성 → config 6키·서비스계정·Resend키 제공 대기. +- **상금 약관(포인트 배점 수치)**: 미정 → 팀 결정 후 dev. R2. -## 4. 다음 세션 할 일 — Firebase 연결 (NEW 프로젝트) +## 5. 다음 세션 할 일 (우선순위) +### 🔴 A. 멀티 경기 구조 (06.09 회의 최대 변경) +- 단일 `/` → **전체 일정 대시보드** + `/match/[matchId]` 상세 + (차기)`/leaderboard` +- `lib/schedule.ts` 6경기를 소스로 대시보드/동적 상세 렌더 +### 🔴 B. 경기 상세 변경 (금요일 핵심) +- 확률 라벨 "승리 예측 팀 승리 확률"로, 닉네임 제거(이메일만), 서브타이틀 삭제, 투표 카운트다운 복원, 예측하기→결과보기 전환, 경기별 공유 +### 🟠 C. Firebase 연결 (BACKEND.md 그대로) +- `npm i firebase`(설치됨) → `.env.local` 6키 → `mockData` 읽기 → Firestore, `Arena.confirmSubmit` mock → `submitPrediction` 콜러블 +- `firebase deploy --only firestore:rules,firestore:indexes` → `node scripts/seed.mjs` → functions 생성/구현 +### 🟡 D. 폴리시 +- Pretendard 웹폰트, OG 동적 공유카드(@vercel/og), 카카오 공유 SDK, 접근성 -### 사용자(나)에게 요청할 것 ⬇ (새 세션이 물어볼 항목) -1. **새 Firebase 프로젝트 생성** 후: `projectId`, 웹앱 config 6키 - (apiKey, authDomain, projectId, storageBucket, messagingSenderId, appId) -2. Firestore 사용 설정(Native 모드), 리전 선택(asia-northeast3 서울 권장) -3. (선택) 결과 알림 이메일 발송용 키(Resend/SendGrid) -4. 도메인 결정(triplepick.ai 등) — 미정이면 Firebase Hosting 기본 도메인 - -### 연결 작업 순서 -1. `npm i firebase` + `lib/firebase.ts`(초기화, env에서 config 로드) -2. `.env.local`에 NEXT_PUBLIC_FIREBASE_* 6키 -3. `lib/mockData.ts` → `lib/data.ts`로 교체: `getMatch / getAIPredictions / getCrowdStats` Firestore 읽기 -4. `components/Experience.tsx`의 `confirmSubmit` mock → `submitPrediction`(Firestore write + 트랜잭션 crowd 증분)으로 교체 -5. Firestore 보안 규칙 작성(기능정의서 §7): 쓰기 검증(마감·outcome·score·nickname), device_id 중복 방지 -6. 시드: matches/ai_predictions 문서 입력(현재 mockData 값 사용) -7. Cloud Functions: `onResultSet → scoreAll`(채점), `subscribeResult`, OG `generateShareCard` -8. GA4 이벤트 연결(기능정의서 §6), UTM 보존 -9. `firebase init hosting` + `next build` 배포 - -### 데이터 모델·API·채점·인수조건 -→ `기능정의서 §3~§8` 그대로 구현. 타입은 `lib/types.ts`에 이미 정의됨. - -## 5. 남은 폴리시/TODO (R1 마감 전) -- [ ] 결과 카드 문구 "선택입니다.나머지" 사이 공백 추가(Experience.tsx) -- [ ] 공유 카드 OG 동적 이미지(@vercel/og) — 현재 텍스트 공유만 -- [ ] 카카오 공유 SDK 연동(국내 1순위) -- [ ] 폰트: Pretendard 웹폰트 로드(현재 시스템 폴백) -- [ ] 국기 이모지 → 에셋 이미지(선택) -- [ ] 마감(lockAt) 지난 상태 QA, device_id 중복 제출 QA -- [ ] 접근성(터치 타깃·대비) 점검 +> 금요일(체코전) 스코프 = **랜딩 + 투표만**. 리더보드·응원가·영상·댓글·AI적중률·공유포인트 = **차기**. ## 6. 주의 -- `lib/mockData.ts`의 AI 예측은 **플레이스홀더**. 발행 전 실제 GPT/Claude/Gemini JSON으로 교체. -- 경로에 공백 없음(`triplepick-web`) — 툴링 안전. 상위 캠페인 폴더는 `Worldcup 2026`(공백 있음)이라 앱을 분리 배치함. +- 룩앤필은 **`docs/DESIGN.md` 토큰을 따른다**(임의 변경 금지). 변경 시 DESIGN.md부터 갱신. +- mockData AI예측은 플레이스홀더 → 실제 모델 출력으로 교체. +- 경로 공백 없음(`triplepick-web`). 상위 캠페인 폴더(`Worldcup 2026`)는 공백 있어 앱 분리 배치. diff --git a/docs/DESIGN.md b/docs/DESIGN.md new file mode 100644 index 0000000..63330a1 --- /dev/null +++ b/docs/DESIGN.md @@ -0,0 +1,100 @@ +# TriplePick — Look & Feel / Design System (SSOT) + +최종 갱신: 2026-06-09 KST +출처: 구현된 `app/globals.css` + 컴포넌트 + 사용자 디렉션(02/03 목업 + 첨부 레퍼런스 반복 확정) +원칙: **이 문서가 룩앤필의 단일 기준(SSOT).** 새 컴포넌트·페이지는 여기 토큰·패턴을 그대로 따른다. + +> 디자인 진화 기록(Before/After): `/before`(v1 다크토큰) vs `/`(현재). 임의 변경 금지 — 토큰을 바꾸면 여기부터 갱신. + +--- + +## 1. 브랜드 에센스 +- 이름: **TriplePick / Triple Pick 2026** · 핸들 `@triplepick_ai` · 운영 AIO2O +- 컨셉: "AI 셋(GPT·Claude·Gemini) vs 너" — 대결·스코어보드·e스포츠 픽밴 에너지 +- 톤: 다크 차콜 베이스 + **민트/틸그린 액센트**, 프리미엄 스포츠 브로드캐스트 +- ⚠️ 금지: **"월드컵" 명칭(법적)** → "글로벌 축구" 프레이밍 / **골드 촌스러운 톤 금지**(쿨 민트·틸) / **단색 초록 일변도 금지** + +--- + +## 2. 컬러 토큰 (globals.css `:root` — 정확값) + +| 역할 | 변수 | HEX | 용도 | +|---|---|---|---| +| 베이스 | `--bg` | `#14171C` | 차콜 다크 배경 | +| 베이스2 | `--bg2` | `#1B1F26` | 카드/입력 배경 | +| 카드 | `--card` | `#20262F` | 다크 카드 | +| **CTA 민트** | `--mint` | `#85FCA8` | 메인 버튼(밝은 민트그린) | +| 민트 텍스트 | `--mint-ink` | `#0A2417` | 민트 버튼 위 글자 | +| 액센트/글로우 | `--green` | `#3EE08A` | VS 글로우·보더·언더라인 | +| **레이어 시트** | `--sheet` | `#F2F3EF` | 화이트 레이어드 시트(AI예측·픽) | +| 시트 카드 | `--sheet-card` | `#FFFFFF` | 시트 위 카드 | +| 잉크 | `--ink` | `#14171C` | 화이트 위 텍스트 | +| 잉크 뮤트 | `--ink-muted` | `#79808C` | 보조 텍스트 | +| 다크 라인 | `--line-d` | `#2B313C` | 다크 보더 | +| 라이트 라인 | `--line-l` | `#E3E5DF` | 시트 보더 | + +**모델 컬러(고정):** GPT `--gpt #15A99B`(틸그린) · Claude `--claude #E8814A`(오렌지) · Gemini `--gemini #5B8CFF`(블루) +**상금 카드(쿨톤, 골드 폐기):** 보더 `--gold-border #5EF0B4` · 배경 `--gold-bg #0E1F1B`(딥틸) · 버튼 그라데 `#85FCA8 → #15A99B`(`.gold-btn`) + +> 배경: `radial-gradient(120% 50% at 50% -5%, #20302A, var(--bg))` — 상단 미세 그린 글로우. + +--- + +## 3. 타이포그래피 +- **타이틀 = Impact** (`.font-impact`: Impact, Haettenschweiler, "Arial Narrow Bold"). 예: `TRIPLE PICK 2026` 44px, 섹션 헤딩 32–40px +- 본문 = Pretendard / 시스템 산세리프 (한글). (Pretendard 웹폰트 로드는 TODO) +- 스케일: **사용자 지시로 2× 상향**. AI 스코어 28px · 모델명 20px · 매치 타이틀 22px · 헤딩 22px · 세그먼트 16px · 스테퍼 32px · CTA 20px +- 후킹 헤드라인: **한 줄 강제**(`whitespace-nowrap`, 19px) + 양옆 `⫽` 그린 + +--- + +## 4. 레이아웃 +- **모바일 우선**, `.shell` max-width **460px**, 좌우 패딩 16 +- 구조 = **다크 히어로(상) + 레이어드 화이트 시트(하)** 하이브리드 + - 다크: Hero · MatchupHUD(대결) + - 화이트 시트(`.sheet`, radius 22, 그림자): AI 예측 + 당신의 선택 + - 다크: 상금(쿨톤) · Crowd · Footer +- 카드 radius 12–22, 터치 타깃 ≥ 44px + +--- + +## 5. 컴포넌트 카탈로그 (현재 구현 = 기준) + +- **Hero** (`Hero.tsx`): `TRIPLE PICK 2026`(Impact, 2026 그린) → `AI Prediction Arena`(그린) → 서브카피 **알약**(반투명, 가시성↑) → 후킹 한 줄(⫽ 그린) +- **MatchupHUD** (`MatchupHUD.tsx`): `Match 01 → Final` 진행 바 → **녹색 글로우 보더 카드**(box-shadow green) → **프로필 로고 아이콘**(`/icons/profile.png`) + 팀명 + 일시(그린 볼드) + 경기장 → 국기(실제 KOR png + CZE 인라인 SVG) + **라이트닝 그린 VS**(Impact, textShadow glow) +- **AI 예측 카드** (`Arena.tsx`, 화이트 시트 내): 실제 모델 아이콘(`/icons/gpt.png·claude.jpg·gemini.jpeg`) + 모델명 + 스코어(28px) + 확률% + **모델색 confidence 바** + 근거 한 줄 +- **세그먼트(당신의 선택)**: 3분할, 선택 시 `--mint` 배경 + 잉크 텍스트 +- **스코어 스테퍼**: −/+ 버튼 + 큰 숫자(32px) +- **CTA(`.btn-mint`)**: `#85FCA8` + 글로우 그림자 +- **상금 카드(`.gold-card`/`.gold-btn`)**: 딥틸 배경 + 민트 보더 + 민트→틸 그라데 버튼 (쿨톤) +- **Crowd Pick**: 가로 세그먼트 단일 바(국기 + %) +- **Footer**: fine print + 법무 4고지 + `© 2026 TriplePick · 운영 AIO2O · @triplepick_ai` + +--- + +## 6. 아이콘·이미지 자산 (`public/icons/`) +- `ball.png` — **클래식 흑백 축구공(투명 PNG)**. ⚠️ 흰 배경/체커보드 구워넣기 금지(마젠타 키잉으로 투명 처리한 자산) +- `profile.png` — TriplePick 브랜드 로고(매치카드 아이콘) +- `gpt.png` / `claude.jpg` / `gemini.jpeg` — 실제 브랜드 아이콘(레터박스 금지) +- `kor.png` — 한국 국기. 체코는 인라인 SVG(백·적 + 청 삼각) +- 글로우는 `drop-shadow(... rgba(62,224,138,.))` 그린으로만 + +--- + +## 7. Do / Don't +- ✅ 실제 모델 아이콘 · 클래식 축구공(투명) · 라이트닝 그린 VS · 화이트 시트 + 다크 하이브리드 · 민트 CTA +- ✅ 3색(틸그린/오렌지/블루)으로 모델 구분, 글로우는 그린 +- ❌ "월드컵" 표현 / 골드 톤 / 단색 초록 일변도 / 레터박스 가짜 아이콘 / 이모지 축구공 / 임의 다크 단색(=v1 회귀) +- ❌ 한글을 이미지로 굽기(가독성↓) — 텍스트 레이어로 + +--- + +## 8. 06.09 회의로 반영해야 할 디자인 변경 (대기) +> 상세: `../../Worldcup 2026/000. Plan/Task — 랜딩·디자인·프론트엔드 업데이트 (06.09 회의).md` +- [ ] 확률 라벨 `Confidence` → **"OO 승리 확률 XX%"**(이긴 팀 기준) + 승리팀 국기 +- [ ] confidence 바 축소/삭제 검토, **근거(메인 리즌) 강조**, 모델 텍스트 짧고 재미있게 +- [ ] **닉네임 필드 제거 → 이메일만** +- [ ] 서브타이틀 `Korea Republic vs Czechia · Matchday Prediction` **삭제(중복)** +- [ ] **"투표 종료까지" 카운트다운** 상세 페이지에 복원 +- [ ] 멀티 경기: 대시보드 + 경기 상세 + (차기)리더보드 — 동일 토큰으로 확장 +- [ ] 예측하기 → (경기 후) **결과 보기** 버튼 + 최종 스코어 표시