다크 모드는 사용자가 어두운 화면을 고른 기기에서 페이지를 어두운 색으로 보이게 만드는 일입니다. 미디어 쿼리 조건 prefers-color-scheme이 dark일 때 :root의 색 변수 값만 바꾸고, color-scheme: light dark로 브라우저가 그리는 입력 칸 모양도 맞춥니다.
밝은 페이지의 바탕만 어둡게 바꾸면 짙은 글자가 바탕에 묻힙니다. 카드 · 입력 칸 · 선 · 링크도 함께 바꿔야 하는데, 규칙마다 어두운 색을 새로 적으면 고칠 곳이 늘고 한 곳만 놓쳐도 그 자리가 읽히지 않습니다. 11단원에서 레시피 노트의 색을 역할 이름의 변수 여섯으로 모았습니다. 이 레슨은 그 여섯의 값만 바꿔 다크 모드를 만들고, 변수로 바꿀 수 없는 브라우저의 칸 모양을 맞추는 한 줄을 봅니다.
그림 준비 중
1어두운 화면 조건에서 변수 값만 바꾼다
밝은 값 둘을 :root에 두고 body가 꺼내 씁니다.
<p>김치볶음밥</p>
:root {
--bg: #f5f5f7;
--text: #1d1d1f;
}
body {
color: var(--text);
background-color: var(--bg);
}body의background-colorrgb(245, 245, 247)p의 글자색rgb(29, 29, 31)
어두운 화면 조건 블록에서 같은 두 변수에 어두운 값을 줍니다. 아래 잰 값은 어두운 화면으로 설정한 브라우저에서 잰 값입니다. 결과 화면은 어두운 쪽을 그리지 않을 수 있습니다 — 어두운 쪽은 위 실험대와 잰 값으로 봅니다.
@media (
prefers-color-scheme: dark
) {
:root {
--bg: #121820;
--text: #f1f4f8;
}
}body의background-colorrgb(18, 24, 32)p의 글자색rgb(241, 244, 248)
body 규칙은 한 글자도 바꾸지 않았는데 바탕과 글자가 함께 바뀌었습니다. var()는 지금의 변수 값을 꺼내기 때문입니다. 두 :root 규칙은 선택자가 같아 명시도가 같고, 조건이 맞으면 나중에 쓴 조건 블록 쪽이 이깁니다(브라우저 규칙 5). 조건이 맞지 않으면 블록이 닿지 않아 블록 밖 :root의 밝은 값이 남습니다.
light 값은 밝은 화면을 골랐거나 따로 고르지 않은 기기입니다. 밝은 값은 블록 밖 :root에 이미 있어 light 블록은 따로 쓰지 않습니다.
2color-scheme으로 브라우저가 그리는 칸도 맞춘다
변수는 우리가 var()로 칠한 곳만 바꿉니다. 입력 칸 · 체크박스 · 라디오 · 스크롤 막대는 브라우저가 기본 모양으로 그려, 어두운 페이지에서도 흰 채로 남습니다. color-scheme: light dark는 이 페이지가 밝은 화면과 어두운 화면을 모두 지원한다고 브라우저에 알립니다 — 그러면 브라우저가 고른 화면에 맞는 칸 모양을 그립니다.
<label for="q">요리 찾기</label>
<input id="q" />
:root {
color-scheme: light dark;
}#q의color-schemelight dark#q의background-colorrgb(59, 59, 59)
어두운 화면에서 입력 칸이 어두운 회색으로 그려졌습니다(크롬 기준 — 칸의 기본 모양은 브라우저마다 다릅니다). 이 줄이 없으면 칸은 흰 바탕 그대로입니다. color-scheme은 물려받는 속성이라 :root에 한 번 두면 안의 모든 칸이 따릅니다(브라우저 규칙 5). 17단원 할 일 화면의 체크박스도 이 줄로 어두워집니다.
비슷해 보이는 셋은 하는 일이 다릅니다.
| 코드 | 하는 일 |
|---|---|
@media (prefers-color-scheme: dark) | 어두운 화면을 골랐을 때 닿을 규칙을 고른다 |
color-scheme: light dark | 두 화면을 지원한다고 알려, 브라우저가 그리는 칸 모양을 맞춘다 |
var(--surface) 같은 꺼내기 | 우리가 정한 변수 값으로 칠한다 |
3개발자 도구로 두 화면을 견준다
어두운 화면은 운영체제의 화면 모양 설정(밝게 · 어둡게)을 따릅니다. 페이지만 견줄 때는 개발자 도구의 Rendering에서 Emulate CSS media feature prefers-color-scheme 목록을 prefers-color-scheme: dark로 골라 흉내 냅니다(크롬 기준 — 여는 길은 움직임 줄이기 3절). 보조 글자 · 입력 칸 · 포커스 윤곽선까지 두 화면에서 읽히는지 보고, 끝나면 No emulation으로 되돌립니다.
예제
레시피 노트 다크 모드 — 하위 목표 셋
다크 모드 틀의 하위 목표는 셋입니다 — 색을 변수로 모으기 → 어두운 값 → color-scheme. 하위 목표 1은 11단원에서 한 일입니다.
| 변수 | 역할 | 밝은 값 | 어두운 값 |
|---|---|---|---|
--bg | 페이지 바탕 | #f5f5f7 | #121820 |
--surface | 바탕 위의 면(카드 · 칸) | #ffffff | #1d2633 |
--text | 글자 | #1d1d1f | #f1f4f8 |
--muted | 보조 글자 | #6e6e73 | #a3adb8 |
--line | 선 | #d2d2d7 | #3a4552 |
--accent | 링크 · 강조 · 단추 | #005bb5 | #9ac7ff |
<div class="card">
<h2>김치볶음밥</h2>
</div>
<a href="kimchi.html">자세히</a>
<label for="q">요리 찾기</label>
<input id="q" />
/* 1. 색을 변수로 모으기 */
:root {
--bg: #f5f5f7;
--surface: #ffffff;
--text: #1d1d1f;
--muted: #6e6e73;
--line: #d2d2d7;
--accent: #005bb5;
}색을 쓰는 곳은 모두 변수에서 꺼냅니다.
body {
color: var(--text);
background-color: var(--bg);
}
.card {
background-color:
var(--surface);
border: 1px solid var(--line);
}
a {
color: var(--accent);
}어두운 화면 조건 블록에서 여섯의 값만 바꿉니다. 카드 · 링크 규칙은 블록 안에 옮겨 적지 않습니다.
/* 2. 어두운 값 */
@media (
prefers-color-scheme: dark
) {
:root {
--bg: #121820;
--surface: #1d2633;
--text: #f1f4f8;
--muted: #a3adb8;
--line: #3a4552;
--accent: #9ac7ff;
}
}body의background-colorrgb(18, 24, 32).card의background-colorrgb(29, 38, 51)a의 글자색rgb(154, 199, 255)#q의background-colorrgb(255, 255, 255)
바탕 · 카드 · 링크는 어두워졌는데 입력 칸만 흰 채입니다. 마지막 한 줄로 칸 모양을 맞춥니다. 첫 :root의 끝에 넣어도 같습니다.
/* 3. color-scheme */
:root {
color-scheme: light dark;
}#q의background-colorrgb(59, 59, 59)
17단원 할 일 화면은 여기에 --danger(밝은 #d70015 · 어두운 #ff6b6b)를 더해 변수 일곱의 값을 바꿉니다.
자주 틀리는 곳
틀린 생각: 어두운 화면은 규칙마다 어두운 색을 새로 적어 만든다.
<p>김치볶음밥</p>
<a href="kimchi.html">자세히</a>
a {
color: #005bb5;
}
@media (
prefers-color-scheme: dark
) {
body {
color: #f1f4f8;
background-color: #121820;
}
}p의 글자색rgb(241, 244, 248)a의 글자색rgb(0, 91, 181)
body만 옮겨 적고 a를 놓쳤습니다. 어두운 바탕 위에 짙은 파랑 링크가 남아 읽기 어렵습니다.
맞는 규칙: 색을 쓰는 규칙은 그대로 두고 변수 값만 바꿉니다. 변수를 꺼내 쓰는 곳은 모두 함께 바뀝니다.
틀린 생각: 변수 이름은 밝은 화면의 색 이름으로 지어도 된다.
:root {
--white: #ffffff;
}
@media (
prefers-color-scheme: dark
) {
:root {
--white: #1d2633;
}
}
어두운 화면에서 --white는 짙은 남색을 담습니다. var(--white)로 칠한 카드를 보며 흰 카드를 떠올리게 돼, 이름이 거짓말을 합니다.
맞는 규칙: 변수 이름은 역할로 짓습니다(--surface). 두 화면에서 값은 달라도 역할은 같습니다.
틀린 생각: color-scheme: light dark만 적으면 페이지가 알아서 어두워진다.
<p>김치볶음밥</p>
:root {
--bg: #f5f5f7;
color-scheme: light dark;
}
body {
background-color: var(--bg);
}body의background-colorrgb(245, 245, 247)p의 글자색rgb(255, 255, 255)
바탕은 우리가 정한 밝은 값 그대로인데, 글자는 브라우저가 어두운 화면에 맞춰 흰색으로 그렸습니다. 밝은 바탕에 흰 글자가 되어 읽히지 않습니다.
맞는 규칙: color-scheme은 브라우저가 그리는 것(처음 글자색 · 칸 모양)만 맞춥니다. 우리가 칠한 색은 어두운 값을 변수로 함께 줍니다 — 하위 목표 2와 3은 둘 다 필요합니다.
확인 문제
1. 예제에서 조건 블록 안 --accent의 값을 #ffd60a(노랑)로 바꿉니다. '자세히' 링크는 두 화면에서 무슨 색일까요?
- ① 어두운 화면에서만 노랑, 밝은 화면은 그대로 파랑
- ② 두 화면 모두 노랑
- ③ 두 화면 모두 그대로
힌트
바꾼 값이 조건 블록 안에 있는지 밖에 있는지 보세요.
정답
①입니다.
<a href="list.html">자세히</a>
:root {
--accent: #005bb5;
}
a {
color: var(--accent);
}a의 글자색rgb(0, 91, 181)
@media (
prefers-color-scheme: dark
) {
:root {
--accent: #ffd60a;
}
}a의 글자색rgb(255, 214, 10)
- 조건 블록은 어두운 화면에서만 닿아, 밝은 화면의
--accent는 블록 밖 값 그대로입니다. - ②는 조건 블록이 늘 닿는다고 본 것입니다.
2. 밝은 값만 있는 페이지에 다크 모드 하위 목표 2를 씁니다. 어두운 화면에서 --bg는 #121820, --text는 #f1f4f8이 되게 ____ 자리에 블록 하나를 쓰세요.
:root {
--bg: #f5f5f7;
--text: #1d1d1f;
}
/* 2. 어두운 값 */
____
힌트
1절 둘째 CSS의 꼴을 따릅니다 — 조건 블록 안에 같은 선택자를 둡니다.
정답
<p>잡채</p>
:root {
--bg: #f5f5f7;
--text: #1d1d1f;
}
body {
color: var(--text);
background-color:
var(--bg);
}/* 2. 어두운 값 */
@media (
prefers-color-scheme: dark
) {
:root {
--bg: #121820;
--text: #f1f4f8;
}
}body의background-colorrgb(18, 24, 32)p의 글자색rgb(241, 244, 248)
- 블록 안 선택자도
:root입니다.body규칙을 블록 안에 옮겨 적을 필요가 없습니다. - 조건 블록은 밝은 값
:root뒤에 둡니다. 위에 두면 나중에 쓴 밝은 값이 이깁니다.
3. 플로우 카페 페이지입니다. 주문 폼에만 color-scheme을 주었습니다. 어두운 화면에서 브라우저가 어두운 칸 모양으로 그리는 칸을 모두 고르세요.
<label>찾기 <input /></label>
<form class="order">
<label>메뉴 <input /></label>
<label>잔 수 <input /></label>
</form>
.order {
color-scheme: light dark;
}- ㉠ 찾기 칸
- ㉡ 메뉴 칸
- ㉢ 잔 수 칸
힌트
color-scheme을 받은 요소와 그 안에 든 요소를 나무에서 찾아보세요.
힌트 둘
color-scheme이 물려받는 속성인지 2절에서 찾아보세요.
정답
㉡ · ㉢입니다.
body > label input의color-schemenormal.order input의color-schemelight dark.order label:last-child input의color-schemelight dark
color-scheme은 물려받는 속성이라form안의 두 칸이 그 값을 받습니다. 폼 밖 찾기 칸은 처음 값normal이라 어두운 화면에서도 밝은 칸 모양입니다.- ㉡만 골랐다면 첫 자식만 물려받는다고, ㉠도 골랐다면 페이지 전체에 닿는다고 본 것입니다. 페이지 전체에 주려면
:root에 둡니다.
컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요.
예제의 HTML과 네 CSS를 index.html과 style.css로 저장하고 link로 이어 엽니다(문서 뼈대의 body 안에 예제 HTML을 넣습니다). 3절대로 개발자 도구에서 어두운 화면을 흉내 내고, 마지막 :root 규칙의 color-scheme 줄을 지운 뒤 저장하고 새로고침합니다. 입력 칸의 모양을 줄을 지우기 전과 견줘 보고, 끝나면 흉내를 No emulation으로 되돌립니다.