Skip to content

feat(blog): 다크모드 도입 — 토큰 기반 테마 전환 (#90)#123

Merged
Malloc72P merged 2 commits into
mainfrom
claude/issue-90-dark-mode
Jun 30, 2026
Merged

feat(blog): 다크모드 도입 — 토큰 기반 테마 전환 (#90)#123
Malloc72P merged 2 commits into
mainfrom
claude/issue-90-dark-mode

Conversation

@Malloc72P

Copy link
Copy Markdown
Owner

디자인 토큰(#94/#120)이 깔린 덕에 컴포넌트 대부분을 건드리지 않고 다크모드를 도입합니다.

동작 원리

Tailwind v4 유틸리티는 var(--color-*)를 참조합니다. 그래서 html.dark에서 색 변수만 덮어쓰면 전체가 전환됩니다.

  • 텍스트 그레이(gray-400~900, black) → 밝게
  • 배경 그레이(gray-50~200) → 어둡게
  • surface/border/brand/blue(링크) → 다크 값
  • 항상 어두운 헤더(ink)·흰 텍스트(text-white)는 별도 토큰이라 그대로 유지

유일한 충돌(--color-white: 배경 vs 흰 글자)만 bg-white → bg-surface(5곳)로 분리했습니다.

구성

  • ThemeToggle: 헤더 버튼(해/달). html.dark + localStorage 토글, themechange 이벤트 발행
  • FOUC 방지: layout <head>에 인라인 스크립트로 paint 전에 테마 결정 + <html suppressHydrationWarning>
  • giscus: 사이트 테마를 동적으로 따라감(themechange 구독)
  • 시스템 기본 존중: localStorage 없으면 prefers-color-scheme

검증

  • 라이트 모드 100% 동일(.dark에서만 오버라이드) — 스크린샷 확인
  • 다크 모드: 랜딩/포스트 상세 스크린샷 확인(배경·텍스트·카드·코드블록 양호)
  • E2E +1(theme: 토글·새로고침 유지) → 총 8개 통과(로컬 dev + CI=true 프로덕션)
  • 하이드레이션 경고 0 / lint 0 / tsc clean

범위 결정

  • 코드블록 테마: shiki github-dark로 빌드타임 고정이라 두 모드 모두 다크 코드블록 유지(라이트에서도 원래 다크였음 → 일관성 유지). 모드별 코드 테마 전환은 런타임 비용이 커 보류.
  • 다크 팔레트 값: 전부 .dark 한 블록의 토큰이라 쉽게 조정 가능.

🤖 Generated with Claude Code

- global.css: --color-surface 토큰 + .dark에서 색 변수 오버라이드(텍스트 그레이는
  밝게, 배경 그레이는 어둡게, surface/border/brand/blue). Tailwind v4 유틸이 var()를
  참조하므로 컴포넌트 대부분 변경 없이 전체가 전환됨
- ThemeToggle: html.dark + localStorage 토글, themechange 이벤트 발행
- layout: FOUC 방지 인라인 스크립트(paint 전 테마 결정) + suppressHydrationWarning
- giscus: 사이트 테마 동적 연동(themechange 구독)
- bg-white → bg-surface(5곳): 흰 텍스트(--color-white)와 충돌하던 surface만 토큰화
- e2e/theme: 토글·새로고침 유지 검증

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
blog Ignored Ignored Preview Jun 30, 2026 12:53am

@Malloc72P

Copy link
Copy Markdown
Owner Author

🔍 코드 리뷰 (self-review)

다크모드가 양쪽 모드에서 정상 동작하고(스크린샷·E2E·프로덕션 빌드 확인), 구현 중 발견한 이슈는 커밋 전에 고쳤습니다. 남은 개선 1건을 반영하겠습니다.

반영할 미흡사항

  1. (낮음) 토글 아이콘 깜빡임: 다크로 로드된 페이지에서 ThemeToggleuseState(false)로 첫 렌더를 light(달 아이콘)로 그린 뒤 useEffect로 보정해, 첫 프레임에 잘못된 아이콘이 잠깐 보입니다(페이지 자체는 FOUC 스크립트로 안 깜빡임). → 아이콘을 JS 상태 대신 CSS dark: 변형으로 토글해 html.dark와 즉시 일치시키겠습니다(플래시 제거 + useEffect 단순화).

구현 중 발견·수정 완료

  • <html> className 하이드레이션 불일치 경고 → suppressHydrationWarning으로 해소
  • JSX 주석 위치 오류(파싱 에러) → // 주석으로 수정

의도적 결정

  • 코드블록 테마는 shiki github-dark 빌드타임 고정 유지(라이트에서도 원래 다크였음). 모드별 전환은 런타임 비용이 커 보류.
  • 다크 팔레트 값은 .dark 한 블록의 토큰이라 손쉽게 조정 가능.

1번 반영 후 별도 코멘트로 정리하겠습니다.

- global.css: @custom-variant dark로 dark: 유틸이 .dark 클래스를 따르게 설정
- ThemeToggle 아이콘을 JS 상태 대신 CSS(dark:)로 토글 → 다크로 로드된 페이지에서
  첫 프레임부터 해/달 아이콘이 html.dark와 일치(플래시 없음). aria-label만 상태 유지

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Malloc72P

Copy link
Copy Markdown
Owner Author

✅ 리뷰 반영 완료 (c714d72)

리뷰 1번(토글 아이콘 플래시) 반영:

  • global.css@custom-variant dark (&:where(.dark, .dark *)) 추가 → dark: 유틸이 OS 설정이 아니라 .dark 클래스를 따름
  • ThemeToggle 아이콘을 JS 상태 대신 CSS dark:로 토글 → 다크로 로드된 페이지에서 첫 프레임부터 해/달 아이콘이 일치(플래시 제거). aria-label만 상태로 유지(SR 전용이라 보이는 깜빡임 없음)

검증

  • 빌드 CSS에서 dark:hidden/block:where(.dark,.dark *)(클래스 기반)로 생성됨 확인(media query 아님)
  • 단위 81 / E2E 8(theme 포함, 로컬 dev) / lint 0 / tsc clean

@Malloc72P Malloc72P merged commit c9cd7bd into main Jun 30, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant