Claude Code 다크 모드 구현 실무 가이드
CSS 변수, 시스템 설정, localStorage, hydration 대응까지 포함한 다크 모드 구현 가이드.
먼저 제품 리스크를 정리하기
Claude Code 다크 모드 구현 실무 가이드의 목적은 화면을 화려하게 만드는 것이 아니라, 전환율, 읽기 쉬움, 모바일 레이아웃, 접근성을 깨지 않으면서 개선하는 것입니다. Claude Code는 빠르게 코드를 만들 수 있지만, 실제 운영에서는 빈 상태, 로딩, 오류, 키보드 포커스, 긴 텍스트, 광고 영역, CTA 위치가 더 중요합니다.
함께 볼 글은 claude code design system, claude code accessibility, claude code code review 입니다. 공식 자료는 Claude Code docs, MDN prefers-color-scheme, MDN color-scheme, WCAG contrast 를 참고하세요. Claude Code에게는 목표, 수정 범위, use case, pitfall, 검증 방법을 명확히 전달해야 합니다.
Claude Code에 주는 프롬프트
가장 작은 안전한 변경으로 구현해 주세요.
기존 컴포넌트 구조, 디자인 토큰, 라우팅을 유지해 주세요.
use case, pitfall, 접근성, 모바일 표시, 실패 상태를 함께 확인해 주세요.
복사해서 실행 가능한 코드와 review checklist를 함께 반환해 주세요.
Use case checklist
- 랜딩 페이지와 글 페이지: 다음 행동을 자연스럽게 보이게 하되 본문과 CTA를 가리지 않습니다.
- SaaS 대시보드: 로딩, 빈 데이터, 오류, 성공 상태가 즉시 이해되어야 합니다.
- 결제, 가입, 상담 흐름: 시각 효과가 주요 버튼보다 강하면 안 됩니다.
- 팀 리뷰: Claude Code가 코드와 검증 항목을 함께 남겨야 리뷰 시간이 줄어듭니다.
구현 코드
:root {
color-scheme: light;
--color-page: #ffffff;
--color-surface: #f8fafc;
--color-text: #0f172a;
--color-muted: #475569;
--color-border: #dbe3ef;
--color-link: #2563eb;
--color-focus: #f59e0b;
}
[data-theme="dark"] {
color-scheme: dark;
--color-page: #0b1120;
--color-surface: #111827;
--color-text: #f8fafc;
--color-muted: #cbd5e1;
--color-border: #334155;
--color-link: #93c5fd;
--color-focus: #fbbf24;
}
body {
background: var(--color-page);
color: var(--color-text);
}
:focus-visible {
outline: 3px solid var(--color-focus);
outline-offset: 3px;
}
const storageKey = "theme";
const root = document.documentElement;
const stored = localStorage.getItem(storageKey);
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
const theme = stored === "light" || stored === "dark" ? stored : prefersDark ? "dark" : "light";
root.dataset.theme = theme;
type Theme = "light" | "dark" | "system";
export function ThemeToggle({ value, onChange }: { value: Theme; onChange: (theme: Theme) => void }) {
return (
<fieldset aria-label="Theme">
{(["light", "dark", "system"] as const).map((theme) => (
<button
key={theme}
type="button"
aria-pressed={value === theme}
onClick={() => onChange(theme)}
>
{theme}
</button>
))}
</fieldset>
);
}
테마 깜빡임(처음 한순간만 색이 다른 문제) 방지하기
다크 모드에서 처음 부딪히는 함정이, 페이지를 열자마자 한순간 밝은 화면이 보였다가 어두워지는 “깜빡임”입니다. 영어로는 FOUC(Flash of Unstyled Content)나 테마 플래시라고 부릅니다. 원인은 HTML이 표시된 뒤에 JavaScript로 테마를 바꾸기 때문입니다. CSS나 React의 로딩을 기다린 다음 data-theme를 붙이면, 그 대기 시간 동안 초기 색(대개 밝은 색)이 보이게 됩니다.
대책은 테마를 정하는 아주 짧은 스크립트를, 본문보다 먼저(<head> 안에서) 동기적으로 실행하는 것입니다. 저장된 설정과 단말 설정만 보고, 화면이 그려지기 전에 data-theme를 확정합니다.
<script>
(function () {
var stored = localStorage.getItem("theme");
var prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
var theme = stored === "light" || stored === "dark" ? stored : prefersDark ? "dark" : "light";
document.documentElement.dataset.theme = theme;
})();
</script>
핵심은 이 스크립트를 가볍게 만들어 본문 렌더링보다 먼저 실행하는 것입니다. 무거운 처리나 React 초기화를 여기에 넣으면 결국 깜빡임이 돌아옵니다. Astro나 Next.js에서도 프레임워크의 하이드레이션(서버에서 만든 HTML에 브라우저 쪽에서 동작을 붙이는 처리)을 기다리지 않고 테마를 확정할 수 있도록, 이 작은 스크립트만은 따로 다룹니다. 서버에서는 단말 설정을 알 수 없으므로, 처음에는 단말 설정을 존중하고 사용자가 전환하면 localStorage에 저장해 다음부터 우선하는 흐름이 다루기 쉽습니다.
대비, 이미지, 그림자를 다크에서도 읽히게 하기
색을 반전하는 것만으로는 다크 모드가 완성되지 않습니다. 밝은 화면에서 충분했던 대비가 어두운 배경에서는 부족해질 수 있습니다. 본문과 배경의 대비비는 WCAG가 제시하는 4.5:1 이상을 기준으로 확인합니다. 특히 옅은 회색 보조 텍스트, 플레이스홀더, 비활성 버튼은 어두운 배경에서 읽기 어려워지기 쉬운 부분입니다.
| 요소 | 라이트에서의 전제 | 다크에서의 조정 |
|---|---|---|
| 보조 텍스트 | 옅은 회색으로 충분 | 명도를 올려 대비 확보 |
| 로고·아이콘 | 흰 배경 전제 | 배경을 깔거나 다크용으로 교체 |
| 그림자 | 짙은 회색으로 경계 표현 | 테두리(border)로 다시 표현 |
| 상태 색 | 선명한 초록·빨강 | 채도를 낮추고 아이콘도 추가 |
흰색을 전제로 만든 로고나 아이콘은 어두운 배경에서 가장자리가 사라집니다. 투과 PNG는 배경색을 깔거나 다크용 대체본을 준비합니다. 그림자에 의존하던 경계는 --color-border 같은 테두리로 다시 표현하면 어느 테마에서도 구조가 보입니다. 색만으로 상태를 전달하지 말고 아이콘이나 라벨도 함께 두면, 대비가 약한 환경이나 색각 특성이 있는 독자에게도 전달됩니다.
Pitfall checklist
- 스크린샷만 보고 판단하면 실제 전환 흐름을 놓칩니다.
- 색상이나 움직임만으로 의미를 전달하면 접근성이 떨어집니다.
- 모바일 375px 폭을 보지 않으면 가로 스크롤이 생길 수 있습니다.
- 빈 데이터, 긴 라벨, 느린 네트워크, 새로고침을 확인하지 않으면 운영 중 오류가 납니다.
- Claude Code가 무관한 파일까지 고치면 diff가 커져 리뷰가 어려워집니다.
검증 방법
구현 후에는 Claude Code에게 review 전용 요청을 다시 보냅니다. 변경 파일, 위험, 브라우저 확인, 수동 확인을 나누어 받습니다. 그 다음 모바일 폭에서 overflow, 코드 블록, CTA, 포커스 표시, 보조 텍스트를 확인합니다. 라이트와 다크를 전환하며 대비가 떨어지는 곳도 함께 점검합니다.
수익화 관점
이 주제는 광고, 상품 카드, 상담 링크, 가격표, 리드 폼 같은 매출 표면과 연결됩니다. 팀의 실제 저장소에 적용하고 싶다면 Claude Code 교육과 상담 페이지에서 반복 가능한 도입 흐름을 설계할 수 있습니다.
추가 리뷰 관점
- 변경 전후 스크린샷을 비교하고 CTA, 광고, 본문, 폼, 코드 블록을 확인합니다.
- Claude Code에게 삭제 가능한 코드, 프로젝트와 맞지 않는 이름, 위험한 가정을 물어봅니다.
- 배포 전 모바일, 데스크톱, 키보드 조작, 느린 네트워크, 빈 데이터, 새로고침을 확인합니다.
실전 검증 메모
구현, review, 모바일 확인을 분리해서 테스트했습니다. 한 번에 크게 바꾸는 방식보다 diff가 작고, 접근성 및 레이아웃 문제를 배포 전에 잡기 쉬웠습니다. 특히 깜빡임 방지 스크립트를 <head>에 먼저 넣고, 다크 배경에서 대비가 떨어지는 보조 텍스트와 그림자를 따로 점검하는 순서가 효과적이었습니다.
무료 PDF: Claude Code 치트시트
이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.
개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.
작성자 소개
Masa
Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.
관련 글
Claude Code에게 파일 하나만 고치게 하는 지시문 작성법
'더 좋게 만들어줘'로 40줄이 바뀐 실패에서 배운, 수정 범위·검증·되돌리기를 묶은 Claude Code용 요청문 템플릿을 소개합니다.
Claude Code 권한 거절에서 복구하기: guardrail 을 약하게 만들지 않는 법
거절된 Claude Code 명령을 이유, 안전한 대안, 증거 명령, 재시도 조건으로 나누는 복구 workflow.
Claude Code Harness Smoke Test: 에이전트를 믿기 전 15분 검증 루프
Claude Code 작업 전에 범위, 금지 영역, 증거 명령, 공개 URL, 수익 CTA를 확인하는 실무 체크입니다.