diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000..e1314c7 --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,11 @@ +language: ko +reviews: + auto_review: + enabled: true + base_branches: + - main + - develop + path_filters: + - "!**/*.test.ts" + - "!**/*.spec.ts" + - "!**/*.stories.ts" diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..cdb7e2e --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,505 @@ +--- +version: 1.0 +name: k-pullup-design-system +description: 대한민국 철봉 지도 — 미니멀 화이트/블랙 베이스에 단일 포인트 컬러로 운동/아웃도어 정체성을 전달하는 디자인 시스템. 깨끗한 캔버스 위에 콘텐츠(사진, 지도, 데이터)가 주인공이 되고, 프라이머리 컬러는 CTA와 핵심 인터랙션에만 절제되게 사용한다. + +colors: + # === Brand & Accent === + primary: "#18181b" + primary-hover: "#09090b" + primary-active: "#000000" + primary-muted: "#e4e4e7" + primary-subtle: "#f4f4f5" + + # === Neutral (Light Mode) === + canvas: "#ffffff" + surface: "#f8fafc" + surface-elevated: "#ffffff" + border: "#e2e8f0" + border-strong: "#cbd5e1" + divider: "#f1f5f9" + + # === Neutral (Dark Mode) === + canvas-dark: "#0f172a" + surface-dark: "#1e293b" + surface-elevated-dark: "#334155" + border-dark: "#334155" + border-strong-dark: "#475569" + divider-dark: "#1e293b" + + # === Text === + ink: "#0f172a" + body: "#334155" + muted: "#64748b" + placeholder: "#94a3b8" + on-primary: "#ffffff" + ink-dark: "#f8fafc" + body-dark: "#cbd5e1" + muted-dark: "#94a3b8" + + # === Semantic === + success: "#16a34a" + success-muted: "#dcfce7" + warning: "#ca8a04" + warning-muted: "#fef9c3" + error: "#dc2626" + error-muted: "#fee2e2" + info: "#18181b" + info-muted: "#e4e4e7" + + # === Scrim & Overlay === + scrim: "rgba(15, 23, 42, 0.6)" + scrim-light: "rgba(15, 23, 42, 0.3)" + +typography: + fontFamily: "'Pretendard Variable', Pretendard, -apple-system, BlinkMacSystemFont, system-ui, Roboto, 'Helvetica Neue', 'Noto Sans KR', sans-serif" + + display-lg: + fontSize: 24px + fontWeight: 700 + lineHeight: 1.3 + letterSpacing: -0.4px + display-md: + fontSize: 20px + fontWeight: 700 + lineHeight: 1.35 + letterSpacing: -0.3px + display-sm: + fontSize: 18px + fontWeight: 600 + lineHeight: 1.4 + letterSpacing: -0.2px + title-lg: + fontSize: 16px + fontWeight: 600 + lineHeight: 1.4 + letterSpacing: -0.1px + title-md: + fontSize: 15px + fontWeight: 600 + lineHeight: 1.4 + letterSpacing: 0 + body-lg: + fontSize: 16px + fontWeight: 400 + lineHeight: 1.6 + letterSpacing: 0 + body-md: + fontSize: 14px + fontWeight: 400 + lineHeight: 1.5 + letterSpacing: 0 + body-sm: + fontSize: 13px + fontWeight: 400 + lineHeight: 1.45 + letterSpacing: 0 + caption: + fontSize: 12px + fontWeight: 500 + lineHeight: 1.4 + letterSpacing: 0 + caption-sm: + fontSize: 11px + fontWeight: 500 + lineHeight: 1.3 + letterSpacing: 0.1px + button-md: + fontSize: 14px + fontWeight: 600 + lineHeight: 1.3 + letterSpacing: 0 + button-sm: + fontSize: 13px + fontWeight: 600 + lineHeight: 1.3 + letterSpacing: 0 + +rounded: + none: 0px + xs: 4px + sm: 8px + md: 12px + lg: 16px + xl: 20px + 2xl: 24px + full: 9999px + +spacing: + xxs: 2px + xs: 4px + sm: 8px + md: 12px + base: 16px + lg: 20px + xl: 24px + 2xl: 32px + 3xl: 40px + section: 48px + +elevation: + none: "none" + xs: "0 1px 2px rgba(0, 0, 0, 0.04)" + sm: "0 1px 3px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04)" + md: "0 4px 6px rgba(0, 0, 0, 0.05), 0 2px 4px rgba(0, 0, 0, 0.03)" + lg: "0 10px 15px rgba(0, 0, 0, 0.06), 0 4px 6px rgba(0, 0, 0, 0.03)" + +components: + button-primary: + backgroundColor: "{colors.primary}" + textColor: "{colors.on-primary}" + typography: "{typography.button-md}" + rounded: "{rounded.md}" + padding: 10px 20px + height: 40px + button-primary-hover: + backgroundColor: "{colors.primary-hover}" + button-primary-active: + backgroundColor: "{colors.primary-active}" + button-secondary: + backgroundColor: "{colors.canvas}" + textColor: "{colors.ink}" + typography: "{typography.button-md}" + rounded: "{rounded.md}" + border: "1px solid {colors.border-strong}" + padding: 10px 20px + height: 40px + button-ghost: + backgroundColor: transparent + textColor: "{colors.ink}" + typography: "{typography.button-md}" + rounded: "{rounded.md}" + padding: 10px 16px + height: 40px + button-danger: + backgroundColor: "{colors.error}" + textColor: "{colors.on-primary}" + typography: "{typography.button-md}" + rounded: "{rounded.md}" + padding: 10px 20px + height: 40px + search-bar: + backgroundColor: "{colors.surface}" + textColor: "{colors.ink}" + typography: "{typography.body-md}" + rounded: "{rounded.lg}" + border: "1px solid {colors.border}" + padding: 12px 16px + height: 44px + marker-card: + backgroundColor: "{colors.canvas}" + textColor: "{colors.ink}" + typography: "{typography.body-md}" + rounded: "{rounded.lg}" + border: "1px solid {colors.border}" + padding: 0 + marker-card-hover: + border: "1px solid {colors.border-strong}" + elevation: "{elevation.sm}" + section-title: + textColor: "{colors.ink}" + typography: "{typography.title-lg}" + badge: + backgroundColor: "{colors.surface}" + textColor: "{colors.muted}" + typography: "{typography.caption}" + rounded: "{rounded.full}" + padding: 4px 10px + badge-primary: + backgroundColor: "{colors.primary-subtle}" + textColor: "{colors.primary}" + typography: "{typography.caption}" + rounded: "{rounded.full}" + padding: 4px 10px + nav-bottom: + backgroundColor: "{colors.canvas}" + textColor: "{colors.muted}" + textColorActive: "{colors.primary}" + border: "1px solid {colors.border}" + height: 56px + input: + backgroundColor: "{colors.canvas}" + textColor: "{colors.ink}" + typography: "{typography.body-md}" + rounded: "{rounded.md}" + border: "1px solid {colors.border}" + padding: 10px 14px + height: 44px + input-focus: + border: "1px solid {colors.primary}" + ring: "0 0 0 3px {colors.primary-muted}" + card-location: + backgroundColor: "{colors.canvas}" + textColor: "{colors.ink}" + rounded: "{rounded.lg}" + border: "1px solid {colors.border}" + elevation: "{elevation.xs}" + modal: + backgroundColor: "{colors.surface-elevated}" + rounded: "{rounded.xl}" + elevation: "{elevation.lg}" + padding: 24px + toast: + backgroundColor: "{colors.ink}" + textColor: "{colors.on-primary}" + typography: "{typography.body-sm}" + rounded: "{rounded.md}" + padding: 12px 16px + divider: + backgroundColor: "{colors.divider}" + height: 1px +--- + +## Overview + +k-pullup(대한민국 철봉 지도)의 디자인 시스템은 **미니멀리스트 화이트/블랙 베이스**에 단일 프라이머리 컬러(#18181b, 차콜)로 포인트를 주는 구조입니다. 콘텐츠(사진, 지도 마커, 사용자 데이터)가 화면의 주인공이 되고, UI 크롬은 최대한 배경으로 빠집니다. + +### Design Principles + +1. **콘텐츠 퍼스트** — 지도, 사진, 위치 데이터가 시각적 무게의 중심. UI 장식은 최소화. +2. **절제된 컬러** — 프라이머리 차콜는 CTA, 활성 상태, 핵심 인터랙션에만 사용. 나머지는 뉴트럴. +3. **깨끗한 공간** — 넉넉한 여백과 1px 보더로 섹션을 구분. 그림자는 계층이 필요할 때만. +4. **다크모드 네이티브** — 라이트/다크 모두 같은 계층 구조를 유지. 다크는 슬레이트 계열. +5. **모바일 우선** — 터치 타겟 44px 이상, 모바일에서 먼저 완성하고 데스크톱으로 확장. + +## Colors + +### Philosophy + +화면의 90%는 화이트(라이트) 또는 다크 슬레이트(다크)로 채워집니다. 프라이머리 차콜(#18181b)는 **CTA 버튼, 활성 탭, 토글 on 상태, 링크** 등 "지금 여기를 눌러라"는 시그널에만 사용합니다. 대부분의 텍스트, 아이콘, 보더는 뉴트럴 슬레이트 팔레트로 처리합니다. + +### Brand & Accent + +| Token | Hex | Use | +|---|---|---| +| `primary` | #18181b | CTA 버튼, 활성 상태, 토글, 링크, 선택된 마커 | +| `primary-hover` | #09090b | 호버 상태 | +| `primary-active` | #000000 | 누름 상태 | +| `primary-muted` | #e4e4e7 | Focus ring, 선택 배경 (연한 그레이) | +| `primary-subtle` | #f4f4f5 | 뱃지 배경, 알림 배경 (가장 연한 그레이) | + +프라이머리 차콜를 선택한 이유: +- 미니멀 흑백 기반으로 콘텐츠에 집중 +- 화이트/블랙 베이스에서 충분한 대비를 확보 (WCAG AA 통과) +- 무채색이라 어떤 콘텐츠 위에서도 방해하지 않음 + +### Surface (Light) + +| Token | Hex | Use | +|---|---|---| +| `canvas` | #ffffff | 페이지 배경, 카드 배경 | +| `surface` | #f8fafc | 입력 필드 배경, 섹션 구분 배경 | +| `surface-elevated` | #ffffff | 모달, 드롭다운 | +| `border` | #e2e8f0 | 카드 보더, 구분선 | +| `border-strong` | #cbd5e1 | 입력 필드 보더, 강조 구분선 | +| `divider` | #f1f5f9 | 리스트 구분선 (미세) | + +### Surface (Dark) + +| Token | Hex | Use | +|---|---|---| +| `canvas-dark` | #0f172a | 페이지 배경 | +| `surface-dark` | #1e293b | 카드 배경, 섹션 배경 | +| `surface-elevated-dark` | #334155 | 모달, 드롭다운 | +| `border-dark` | #334155 | 카드 보더 | +| `border-strong-dark` | #475569 | 입력 필드 보더 | +| `divider-dark` | #1e293b | 리스트 구분선 | + +### Text + +| Token | Light | Dark | Use | +|---|---|---|---| +| `ink` | #0f172a | #f8fafc | 헤드라인, 강조 텍스트 | +| `body` | #334155 | #cbd5e1 | 본문 | +| `muted` | #64748b | #94a3b8 | 보조 텍스트, 캡션, 비활성 | +| `placeholder` | #94a3b8 | #64748b | 입력 필드 placeholder | + +### Semantic + +| Token | Hex | Use | +|---|---|---| +| `success` | #16a34a | 성공 메시지, 완료 상태 | +| `warning` | #ca8a04 | 경고 | +| `error` | #dc2626 | 에러, 삭제 | +| `info` | #18181b | 정보 알림 (= primary) | + +각 시맨틱 색상은 `-muted` 변형(연한 배경용)을 함께 제공합니다. + +## Typography + +### Font Family + +``` +'Pretendard Variable', Pretendard, -apple-system, BlinkMacSystemFont, +system-ui, Roboto, 'Helvetica Neue', 'Noto Sans KR', sans-serif +``` + +Pretendard를 기본으로 사용합니다. 한/영 모두 깔끔하게 처리되고, Variable Font로 weight 제어가 유연합니다. + +### Scale + +| Token | Size | Weight | Use | +|---|---|---|---| +| `display-lg` | 24px | 700 | 페이지 메인 타이틀 | +| `display-md` | 20px | 700 | 섹션 타이틀 | +| `display-sm` | 18px | 600 | 서브 섹션 타이틀 | +| `title-lg` | 16px | 600 | 카드 타이틀, 섹션 헤드 | +| `title-md` | 15px | 600 | 리스트 아이템 타이틀 | +| `body-lg` | 16px | 400 | 긴 본문 | +| `body-md` | 14px | 400 | 기본 본문 | +| `body-sm` | 13px | 400 | 보조 텍스트, 메타 | +| `caption` | 12px | 500 | 뱃지, 라벨, 타임스탬프 | +| `caption-sm` | 11px | 500 | 마이크로 라벨 | +| `button-md` | 14px | 600 | 버튼 라벨 | +| `button-sm` | 13px | 600 | 작은 버튼 라벨 | + +### Principles + +- Display는 700 이하로 유지. 타이포보다 콘텐츠(사진, 지도)가 시각적 무게를 담당. +- body 텍스트는 400 weight만 사용. 강조가 필요하면 600으로 올리지, 볼드(700)를 남용하지 않음. +- line-height는 1.3~1.6 범위. 작은 텍스트일수록 line-height를 높여 가독성 확보. + +## Layout + +### Spacing + +4px 기반 시스템. 주요 간격: + +- 요소 내부 패딩: `sm`(8px) ~ `base`(16px) +- 섹션 간 여백: `section`(48px) +- 카드 내부: `base`(16px) ~ `lg`(20px) +- 인라인 요소 간 간격: `xs`(4px) ~ `sm`(8px) + +### Responsive + +| Breakpoint | Width | 설명 | +|---|---|---| +| Mobile | < 484px | 기본. 사이드 패널이 바텀시트로 동작 | +| Desktop | ≥ 485px | 좌측 사이드 패널(max-w-96) + 지도 | + +모바일 우선 설계. 사이드 패널 최대 너비 384px(max-w-96)로 콘텐츠 밀도 유지. + +### Grid + +- 카드 목록: 1열 기본, 이미지 갤러리는 2열 masonry +- 수평 스크롤: 마커 카드, 모먼트 썸네일 +- 리스트: full-width, 1px divider로 구분 + +## Elevation + +그림자 사용은 **최소한**으로: + +| Level | Use | +|---|---| +| `none` | 기본 (카드, 버튼, 대부분의 요소) | +| `xs` | 플로팅 위치 뱃지 | +| `sm` | 카드 호버 상태 | +| `md` | 드롭다운, 팝오버 | +| `lg` | 모달, 오버레이 | + +기본 상태에서는 **1px 보더**로 구분하고, 그림자는 계층 전환(호버, 떠오름)에만 사용합니다. + +## Components + +### Buttons + +- **Primary**: 블루 배경 + 흰 텍스트. CTA에만 사용. 한 화면에 1개가 이상적. +- **Secondary**: 흰 배경 + 보더 + 잉크 텍스트. 보조 액션. +- **Ghost**: 배경 없음. 텍스트만. 덜 중요한 액션, 취소. +- **Danger**: 빨간 배경. 삭제, 신고 등 파괴적 액션. + +모든 버튼: 높이 40px, rounded 12px, font-weight 600. + +### Cards + +- **Marker Card**: 사진 + 주소 + 거리. 흰 배경, 1px 보더, rounded 16px. 호버 시 보더 강화 + 미세 그림자. +- **Location Card**: 위치 상세 정보. 시설 뱃지, 설명, 댓글 수. +- 카드 내부 여백은 일관되게 16~20px. + +### Navigation + +- **Bottom Nav**: 흰 배경(다크: 슬레이트), 상단 1px 보더. 아이콘 + 라벨. 활성 탭만 프라이머리 차콜. +- **Header**: 타이틀 중앙 정렬. 좌측 뒤로가기, 우측 액션 아이콘. + +### Search + +- 검색 바: rounded 16px, surface 배경, 1px 보더. 높이 44px. +- 검색 결과: 리스트 형태, 1px divider 구분. + +### Forms + +- Input: 높이 44px, rounded 12px, 1px 보더. Focus 시 프라이머리 보더 + 3px 링. +- Textarea: 동일 스타일, 높이 가변. + +### Feedback + +- **Toast**: 다크 배경(ink) + 흰 텍스트. 하단 중앙. rounded 12px. +- **Badge**: 연한 배경 + 진한 텍스트. 작고 둥글게(full round). +- **Skeleton**: surface 색상으로 실제 레이아웃과 동일한 구조. + +## Interaction & Motion + +### Principles + +- 기본 transition: `150ms ease-out` +- 호버 효과는 보더 강화 또는 미세 그림자만. 배경색 변화 최소화. +- Active/press: `scale(0.98)` 또는 `scale(0.99)` — 미세한 눌림 피드백. +- 페이지 전환: `opacity + translateY(4px)`, 180ms. +- `prefers-reduced-motion` 시 모든 모션 비활성화. + +### Touch + +- 최소 터치 타겟: 44×44px +- Active 상태를 반드시 제공 (hover 의존 금지) +- 스와이프 가능한 영역에 시각적 힌트 제공 (잘림, 그라디언트 마스크) + +## Dark Mode + +### Philosophy + +다크모드는 단순 색 반전이 아닌, **동일한 계층 구조를 다크 팔레트로 재해석**합니다. + +- 배경: 슬레이트 900(#0f172a) → 슬레이트 800(#1e293b) → 슬레이트 700(#334155) 순으로 elevation 표현 +- 텍스트: 밝은 회색 계열. 순백(#fff)은 가장 강조 텍스트에만. +- 프라이머리: 동일 블루 유지. 다크 위에서 더 밝게 보이므로 별도 보정 불필요. +- 보더: 슬레이트 700(#334155) 기본 → 슬레이트 600(#475569) 강조. + +### 대비 확보 + +모든 텍스트/배경 조합은 최소 WCAG AA(4.5:1) 충족: +- ink-dark(#f8fafc) on canvas-dark(#0f172a) → 15.4:1 ✓ +- muted-dark(#94a3b8) on canvas-dark(#0f172a) → 5.7:1 ✓ +- primary-light(#a1a1aa) on canvas-dark(#0f172a) → 7.2:1 ✓ (다크모드에서는 primary-light 사용) + +## Implementation Notes + +### Tailwind Integration + +이 디자인 시스템은 Tailwind CSS의 `@theme` 디렉티브로 구현됩니다: + +```css +@theme { + --color-primary: #18181b; + --color-primary-hover: #09090b; + --color-primary-active: #000000; + --color-primary-muted: #e4e4e7; + --color-primary-subtle: #f4f4f5; + /* ... */ +} +``` + +### Migration from Current Tokens + +| 현재 토큰 | 새 토큰 | 비고 | +|---|---|---| +| `--color-primary: #6c705e` | `--color-primary: #18181b` | 올리브 → 블루 | +| `--color-surface: #f3ede5` | `--color-surface: #f8fafc` | 웜 베이지 → 쿨 슬레이트 | +| `--color-text-on-surface: #404038` | `--color-ink: #0f172a` | 웜 다크 → 쿨 다크 | +| `--color-search-input-bg: #e4e8de` | `--color-surface: #f8fafc` | 통합 | +| `--color-location-badge-bg` | 제거 (범용 토큰 사용) | 특수 토큰 → 범용화 | + +### Known Gaps + +- **지도 마커 색상**: Kakao Maps 위 마커 디자인은 이 시스템의 primary를 따르되, 지도 타일과의 가시성은 별도 검증 필요. +- **이미지 위 텍스트**: 사진 위 오버레이 텍스트의 가독성은 scrim 처리로 보장 — 구체적 scrim opacity는 구현 시 조정. +- **애니메이션 디테일**: 페이지 전환, 모달 진입 등 복잡한 모션은 구현 단계에서 세부 조정. diff --git a/__tests__/lib/challenge-storage-roundtrip.property.test.ts b/__tests__/lib/challenge-storage-roundtrip.property.test.ts index 3fd8e8b..d4efc69 100644 --- a/__tests__/lib/challenge-storage-roundtrip.property.test.ts +++ b/__tests__/lib/challenge-storage-roundtrip.property.test.ts @@ -51,7 +51,19 @@ describe("Feature: challenge-local-streak, Property 2: Schema structure round-tr // Arbitrary for ChallengeData const challengeDataArb: fc.Arbitrary = fc.record({ version: fc.integer({ min: 1, max: 10 }), - lastSyncedAt: fc.oneof(fc.constant(null), fc.date().map((d) => d.toISOString())), + lastSyncedAt: fc.oneof( + fc.constant(null), + fc.tuple( + fc.integer({ min: 2020, max: 2030 }), + fc.integer({ min: 1, max: 12 }), + fc.integer({ min: 1, max: 28 }), + fc.integer({ min: 0, max: 23 }), + fc.integer({ min: 0, max: 59 }), + fc.integer({ min: 0, max: 59 }) + ).map(([y, mo, d, h, mi, s]) => + `${y}-${String(mo).padStart(2, "0")}-${String(d).padStart(2, "0")}T${String(h).padStart(2, "0")}:${String(mi).padStart(2, "0")}:${String(s).padStart(2, "0")}.000Z` + ) + ), records: fc.array(visitRecordArb, { minLength: 0, maxLength: 50 }), streakData: streakDataArb, goalSettings: goalSettingsArb, diff --git a/app/(home)/loading.tsx b/app/(home)/loading.tsx index 98cb1da..562c850 100644 --- a/app/(home)/loading.tsx +++ b/app/(home)/loading.tsx @@ -53,7 +53,7 @@ const Loading = () => { {Array.from({ length: 2 }).map((_, index) => (
diff --git a/app/(home)/page.tsx b/app/(home)/page.tsx index 2206a24..920bca8 100644 --- a/app/(home)/page.tsx +++ b/app/(home)/page.tsx @@ -26,42 +26,22 @@ const Home = async () => { return ( - +
+ +
-
+ + +
- {/*
- -
*/} - - {/* */} - - {/*
- -
- -
- -
*/} - - - {!isNewPicturesError(images) && ( )} - {/*
- - -
*/} -
diff --git a/app/apple-icon.png b/app/apple-icon.png new file mode 100644 index 0000000..caaaa08 Binary files /dev/null and b/app/apple-icon.png differ diff --git a/app/assets/Pretendard-SemiBold.woff b/app/assets/Pretendard-SemiBold.woff new file mode 100644 index 0000000..682e7a4 Binary files /dev/null and b/app/assets/Pretendard-SemiBold.woff differ diff --git a/app/challenge/loading.tsx b/app/challenge/loading.tsx index 2f3bb34..2c2f4d4 100644 --- a/app/challenge/loading.tsx +++ b/app/challenge/loading.tsx @@ -12,7 +12,7 @@ const Loading = () => { return (
-
+
diff --git a/app/error.tsx b/app/error.tsx index c217316..3b5e72c 100644 --- a/app/error.tsx +++ b/app/error.tsx @@ -24,12 +24,12 @@ const Error = ({ return (
-
-
+
+
-
-
+
+
{ return (
-
+
@@ -21,7 +21,7 @@ const Loading = () => {
-
+
diff --git a/app/moments/page.tsx b/app/moments/page.tsx index 58745ca..801ab52 100644 --- a/app/moments/page.tsx +++ b/app/moments/page.tsx @@ -28,7 +28,7 @@ const MomentsPage = async () => { deviceType={deviceType} >
-
+
{ {Array.from({ length: 5 }).map((_, index) => (
diff --git a/app/mypage/config/loading.tsx b/app/mypage/config/loading.tsx index 532f639..75c41f3 100644 --- a/app/mypage/config/loading.tsx +++ b/app/mypage/config/loading.tsx @@ -13,7 +13,7 @@ const Loading = () => {
-
+
@@ -23,12 +23,12 @@ const Loading = () => {
-
-
+
+
-
+
@@ -41,7 +41,7 @@ const Loading = () => {
-
+
diff --git a/app/mypage/loading.tsx b/app/mypage/loading.tsx index 577f186..4fabee7 100644 --- a/app/mypage/loading.tsx +++ b/app/mypage/loading.tsx @@ -19,7 +19,7 @@ const Loading = () => { bodyStyle="pb-0 flex flex-col" >
-
+
@@ -28,14 +28,14 @@ const Loading = () => {
-
+
-
+
@@ -46,7 +46,7 @@ const Loading = () => {
-
+
diff --git a/app/mypage/locate/loading.tsx b/app/mypage/locate/loading.tsx index 9789704..ec041f1 100644 --- a/app/mypage/locate/loading.tsx +++ b/app/mypage/locate/loading.tsx @@ -13,7 +13,7 @@ const Loading = () => { return (
-
+
@@ -24,7 +24,7 @@ const Loading = () => { {Array.from({ length: 5 }).map((_, index) => (
diff --git a/app/mypage/locate/page.tsx b/app/mypage/locate/page.tsx index 11ab551..96bca07 100644 --- a/app/mypage/locate/page.tsx +++ b/app/mypage/locate/page.tsx @@ -59,7 +59,7 @@ const RankingPage = async () => { deviceType={deviceType} >
-
+
등록한 위치 diff --git a/app/mypage/myreport/loading.tsx b/app/mypage/myreport/loading.tsx index 1416888..e0c1bc9 100644 --- a/app/mypage/myreport/loading.tsx +++ b/app/mypage/myreport/loading.tsx @@ -13,7 +13,7 @@ const Loading = () => { return (
-
+
@@ -21,7 +21,7 @@ const Loading = () => { {Array.from({ length: 3 }).map((_, sectionIdx) => (
-
+
@@ -30,7 +30,7 @@ const Loading = () => { {Array.from({ length: 2 }).map((__, cardIdx) => (
diff --git a/app/mypage/myreport/myreport-client.tsx b/app/mypage/myreport/myreport-client.tsx index ed36c6b..c43ccf1 100644 --- a/app/mypage/myreport/myreport-client.tsx +++ b/app/mypage/myreport/myreport-client.tsx @@ -152,7 +152,7 @@ const MyreportClient = ({ bodyStyle="flex flex-col pb-0" >
-
+
받은 제안 @@ -163,7 +163,7 @@ const MyreportClient = ({
-
+
수정 요청 정보 @@ -225,7 +225,7 @@ const MyreportClient = ({ router.push(`/pullup/${markerId}`)} - containerStyle="mt-auto border-t border-primary/10 dark:border-grey-dark" + containerStyle="mt-auto border-t border-border dark:border-grey-dark" > 위치 상세 보기 @@ -243,7 +243,7 @@ const MyreportClient = ({ deviceType={deviceType} >
-
+
내 위치에 들어온 수정 제안 @@ -257,7 +257,7 @@ const MyreportClient = ({ {markerGroups.map(([key, { markerID, reports, address }]) => { return (
-
+
{address || "주소 제공 안됨"} @@ -271,7 +271,7 @@ const MyreportClient = ({ {reports.map((report) => (
-
+
내 정보 관리 @@ -82,7 +82,7 @@ const Mypage = async () => { 설정 @@ -94,14 +94,14 @@ const Mypage = async () => { {/* 기여 등급 */} {!noUser && (
-
+
기여 등급
-
-
+
+
등급 { return (
-
+
@@ -24,7 +24,7 @@ const Loading = () => { {Array.from({ length: 4 }).map((_, index) => (
diff --git a/app/mypage/report/report-client.tsx b/app/mypage/report/report-client.tsx index 36d2064..647f9cd 100644 --- a/app/mypage/report/report-client.tsx +++ b/app/mypage/report/report-client.tsx @@ -53,7 +53,7 @@ const ReportClient = ({ deviceType={deviceType} >
-
+
내가 제안한 수정 목록 diff --git a/app/mypage/user/loading.tsx b/app/mypage/user/loading.tsx index 73593b5..cc57773 100644 --- a/app/mypage/user/loading.tsx +++ b/app/mypage/user/loading.tsx @@ -13,7 +13,7 @@ const Loading = () => { return (
-
+
@@ -24,7 +24,7 @@ const Loading = () => {
-
+
@@ -34,7 +34,7 @@ const Loading = () => {
-
+
diff --git a/app/not-found.tsx b/app/not-found.tsx index 8676c84..d74ffd1 100644 --- a/app/not-found.tsx +++ b/app/not-found.tsx @@ -13,12 +13,12 @@ const NotFound = () => { return (
-
-
+
+
{ return (
-
+
{
-
+
{ >
-
+
-
+
-
+
@@ -39,7 +39,7 @@ const Loading = () => {
-
+
@@ -51,7 +51,7 @@ const Loading = () => {
-
+
diff --git a/app/pullup/[id]/loading.tsx b/app/pullup/[id]/loading.tsx index 9e9c8f0..5f221ad 100644 --- a/app/pullup/[id]/loading.tsx +++ b/app/pullup/[id]/loading.tsx @@ -43,7 +43,7 @@ const Loading = () => { -
+
diff --git a/app/pullup/[id]/moment/loading.tsx b/app/pullup/[id]/moment/loading.tsx index 8e80830..1f78c1b 100644 --- a/app/pullup/[id]/moment/loading.tsx +++ b/app/pullup/[id]/moment/loading.tsx @@ -46,7 +46,7 @@ const Loading = () => { } >
-
+
diff --git a/app/pullup/[id]/moment/moment-client.tsx b/app/pullup/[id]/moment/moment-client.tsx index 227851e..3b039af 100644 --- a/app/pullup/[id]/moment/moment-client.tsx +++ b/app/pullup/[id]/moment/moment-client.tsx @@ -140,8 +140,8 @@ const MomentClient = ({ className="hidden" />
-
-
+
+
-
+
{loading ? "이미지 처리 중..." : "오늘의 운동 순간을 공유해보세요."} diff --git a/app/pullup/[id]/pullup-client.tsx b/app/pullup/[id]/pullup-client.tsx index b4d802c..9520f6f 100644 --- a/app/pullup/[id]/pullup-client.tsx +++ b/app/pullup/[id]/pullup-client.tsx @@ -143,21 +143,21 @@ const PullupClient = ({ (철봉.quantity <= 0 && 평행봉.quantity <= 0 && ( ))} {철봉 && 철봉.quantity > 0 && ( )} {평행봉 && 평행봉.quantity > 0 && ( )} @@ -201,7 +201,7 @@ const PullupClient = ({
{marker.username && ( -
+
@@ -244,10 +244,10 @@ const PullupClient = ({ setActiveIndex(index); }} className={` - grow whitespace-nowrap border-b-2 px-4 py-2 text-[15px] font-semibold transition-[color,border-color,background-color,transform] duration-180 ease-out active:scale-[0.99] active:bg-search-input-bg/55 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-primary/35 dark:active:bg-grey-dark/35 + grow whitespace-nowrap border-b-2 px-4 py-2 text-[15px] font-semibold transition-[color,border-color,background-color,transform] duration-180 ease-out active:scale-[0.99] active:bg-surface/55 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-primary/35 dark:active:bg-grey-dark/35 ${ activeIndex === index - ? "border-primary-dark text-text-on-surface dark:text-grey-light" + ? "border-primary/40 text-text-on-surface dark:text-grey-light" : "border-transparent text-grey-dark dark:text-grey" } `} @@ -262,9 +262,9 @@ const PullupClient = ({ {providerInfo.slice(0, 3).map((comment) => (
-
+
{(user?.chulbong || user?.userId === comment.userId) && (
diff --git a/app/pullup/[id]/report/loading.tsx b/app/pullup/[id]/report/loading.tsx index 3c6cfd4..48789ee 100644 --- a/app/pullup/[id]/report/loading.tsx +++ b/app/pullup/[id]/report/loading.tsx @@ -29,7 +29,7 @@ const Loading = () => {
- +
@@ -46,8 +46,8 @@ const Loading = () => {
- - + + @@ -55,7 +55,7 @@ const Loading = () => {
-
+
diff --git a/app/pullup/[id]/report/report-client.tsx b/app/pullup/[id]/report/report-client.tsx index 225c4d5..e2ddcdf 100644 --- a/app/pullup/[id]/report/report-client.tsx +++ b/app/pullup/[id]/report/report-client.tsx @@ -297,7 +297,7 @@ const ReportClient = ({ placeholder="해당 위치에 대한 설명을 40자 이내로 작성해주세요." value={reportValue.description} onChange={handleDescChange} - className="bg-search-input-bg/45 dark:bg-black/35 dark:border-grey-dark/90" + className="bg-surface/45 dark:bg-black-light dark:border-grey-dark/90" /> 위치가 정확하지 않나요? -
+
현재 위치:{" "} @@ -333,7 +333,7 @@ const ReportClient = ({ {marker.address}
-
+
{changeAddr && ( <> @@ -356,7 +356,7 @@ const ReportClient = ({ > 위치 변경하기 - + 지도에서 위치를 수정할 위치를 클릭해 주세요!
{
-
+
diff --git a/app/search/around/around-client.tsx b/app/search/around/around-client.tsx index 7bc1e3d..7af0876 100644 --- a/app/search/around/around-client.tsx +++ b/app/search/around/around-client.tsx @@ -27,8 +27,8 @@ const AroundClient = ({ deviceType }: Props) => { className={cn( "relative isolate overflow-hidden", "p-6 rounded-2xl border border-white/70 dark:border-white/10", - "bg-search-input-bg/65 dark:bg-black/32 backdrop-blur-md", - "shadow-[0_10px_24px_rgba(64,64,56,0.08)] dark:shadow-[0_10px_24px_rgba(0,0,0,0.3)]", + "bg-surface/65 dark:bg-black-light/90 backdrop-blur-md", + "shadow-[0_10px_24px_rgba(15,23,42,0.06)] dark:shadow-[0_10px_24px_rgba(0,0,0,0.3)]", "flex flex-col items-center text-center mt-6" )} > @@ -36,8 +36,6 @@ const AroundClient = ({ deviceType }: Props) => { aria-hidden className={cn( "absolute inset-0 pointer-events-none", - "bg-linear-to-br from-white/35 via-transparent to-primary/10", - "dark:from-white/8 dark:to-primary-dark/20" )} />
diff --git a/app/search/loading.tsx b/app/search/loading.tsx index da589da..3ca414b 100644 --- a/app/search/loading.tsx +++ b/app/search/loading.tsx @@ -26,7 +26,7 @@ const Loading = () => { aria-label="검색 로딩 카드" className={cn( "w-full rounded-2xl border border-white/70 dark:border-white/10", - "bg-search-input-bg/70 dark:bg-black/35 p-4" + "bg-surface/70 dark:bg-black-light p-4" )} >
@@ -50,7 +50,7 @@ const Loading = () => { className={cn( "rounded-2xl overflow-hidden border", "border-white/70 dark:border-white/10", - "bg-search-input-bg/55 dark:bg-black/25" + "bg-surface/55 dark:bg-black/25" )} > {Array.from({ length: 6 }).map((_, index) => ( diff --git a/app/search/search-client.tsx b/app/search/search-client.tsx index 41ce6cd..8b247c2 100644 --- a/app/search/search-client.tsx +++ b/app/search/search-client.tsx @@ -187,36 +187,18 @@ const SearchClient = ({ className={cn( "relative isolate overflow-hidden", "w-full p-4 rounded-2xl border border-white/70 dark:border-white/10", - "bg-search-input-bg/70 dark:bg-black/35 backdrop-blur-md", - "shadow-[0_8px_24px_rgba(64,64,56,0.08)] dark:shadow-[0_8px_24px_rgba(0,0,0,0.28)]", + "bg-surface/70 dark:bg-black-light backdrop-blur-md", + "shadow-[0_8px_24px_rgba(15,23,42,0.06)] dark:shadow-[0_8px_24px_rgba(0,0,0,0.28)]", "flex items-center gap-3 text-left group", "transition-[transform,box-shadow,border-color] duration-180 ease-out motion-reduce:transition-none", "hover:border-primary/45 dark:hover:border-primary-light/35", - "hover:shadow-[0_12px_28px_rgba(64,64,56,0.14)] dark:hover:shadow-[0_12px_28px_rgba(0,0,0,0.35)]", + "hover:shadow-[0_12px_28px_rgba(15,23,42,0.10)] dark:hover:shadow-[0_12px_28px_rgba(0,0,0,0.35)]", "active:scale-[0.99] active:border-primary/60 dark:active:border-primary-light/40", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/35 dark:focus-visible:ring-primary-light/35", "focus-visible:ring-offset-2 focus-visible:ring-offset-side-main dark:focus-visible:ring-offset-black" )} > -
-
- -
+
@@ -252,7 +234,7 @@ const SearchClient = ({ aria-label="최근 검색 전체 삭제" className={cn( "rounded-full px-3 py-1.5", - "bg-search-input-bg/70 dark:bg-white/5", + "bg-surface/70 dark:bg-white/5", "border border-white/70 dark:border-white/10", "transition-all duration-180 ease-out motion-reduce:transition-none", "hover:border-primary/40 dark:hover:border-primary-light/35", @@ -273,7 +255,7 @@ const SearchClient = ({ className={cn( "mt-3 rounded-2xl overflow-hidden", "border border-white/70 dark:border-white/10", - "bg-search-input-bg/55 dark:bg-black/25", + "bg-surface/55 dark:bg-black/25", "backdrop-blur-sm" )} > @@ -352,7 +334,7 @@ const SearchClient = ({ className={cn( "w-full mt-3 py-2.5 rounded-xl", "border border-white/70 dark:border-white/10", - "bg-search-input-bg/70 dark:bg-white/5", + "bg-surface/70 dark:bg-white/5", "text-text-on-surface-muted dark:text-grey-light", "transition-all duration-180 ease-out motion-reduce:transition-none", "hover:border-primary/45 dark:hover:border-primary-light/35 hover:bg-white/50 dark:hover:bg-white/8", @@ -373,7 +355,7 @@ const SearchClient = ({ className={cn( "relative isolate overflow-hidden mt-3 rounded-2xl p-3 space-y-2.5", "border border-white/70 dark:border-white/10", - "bg-search-input-bg/55 dark:bg-black/25", + "bg-surface/55 dark:bg-black/25", "backdrop-blur-sm" )} > @@ -381,8 +363,6 @@ const SearchClient = ({ aria-hidden className={cn( "absolute inset-0 pointer-events-none", - "bg-linear-to-br from-white/35 via-transparent to-primary/10", - "dark:from-white/8 dark:to-primary-dark/18" )} />
diff --git a/app/signin/email/loading.tsx b/app/signin/email/loading.tsx index 4f801bf..5e4351f 100644 --- a/app/signin/email/loading.tsx +++ b/app/signin/email/loading.tsx @@ -16,11 +16,11 @@ const Loading = () => {
- + - +
diff --git a/app/social/chat/[code]/chat-detail-client.tsx b/app/social/chat/[code]/chat-detail-client.tsx index bad34a8..b45b14c 100644 --- a/app/social/chat/[code]/chat-detail-client.tsx +++ b/app/social/chat/[code]/chat-detail-client.tsx @@ -154,7 +154,7 @@ const ChatDetailClient = ({
{isLoading && (
-
+
채팅방에 접속 중입니다. @@ -164,7 +164,7 @@ const ChatDetailClient = ({ )} {isConnectionError && !isLoading ? (
-
+
연결 실패
@@ -214,8 +214,8 @@ const ChatDetailClient = ({ })}
-
-
+
+
- + {message.userNickname}님이 참여하였습니다.
@@ -269,7 +269,7 @@ const MessageBubble = ({ if (message.message?.includes("님이 퇴장하셨습니다.")) { return (
- + {message.userNickname}님이 나가셨습니다.
@@ -293,7 +293,7 @@ const MessageBubble = ({ className={`rounded-2xl px-4 py-2 text-sm shadow-xs ${ isMine ? "bg-primary text-white dark:bg-primary-dark" - : "border border-grey-light/85 bg-search-input-bg/70 text-text-on-surface dark:border-grey-dark/80 dark:bg-black/35 dark:text-grey-light" + : "border border-grey-light/85 bg-surface/70 text-text-on-surface dark:border-grey-dark/80 dark:bg-black-light dark:text-grey-light" }`} > {message.message} diff --git a/app/social/chat/[code]/error.tsx b/app/social/chat/[code]/error.tsx index 86f11c9..0704c25 100644 --- a/app/social/chat/[code]/error.tsx +++ b/app/social/chat/[code]/error.tsx @@ -15,12 +15,12 @@ const ErrorPage = ({ return (
-
-
+
+
{ >
-
+
@@ -49,8 +49,8 @@ const Loading = ({ params }: { params: { code: string } }) => {
-
-
+
+
diff --git a/app/social/loading.tsx b/app/social/loading.tsx index 939946f..297c741 100644 --- a/app/social/loading.tsx +++ b/app/social/loading.tsx @@ -33,9 +33,9 @@ const Loading = () => { {Array.from({ length: 4 }).map((_, index) => (
-
+
@@ -53,7 +53,7 @@ const Loading = () => {
-
+
@@ -65,7 +65,7 @@ const Loading = () => {
-
+
diff --git a/components/common/badge.tsx b/components/common/badge.tsx index 87db804..3387a3e 100644 --- a/components/common/badge.tsx +++ b/components/common/badge.tsx @@ -47,8 +47,8 @@ const Badge = ({ diff --git a/components/common/bottom-sheet.tsx b/components/common/bottom-sheet.tsx index f8c8ab4..e3781c9 100644 --- a/components/common/bottom-sheet.tsx +++ b/components/common/bottom-sheet.tsx @@ -43,7 +43,7 @@ const BottomSheet = ({