본문으로 건너뛰기

단원 연습 문제

HTML·CSS 목차움직임과 다크 모드문제 12개

16단원 '움직임과 다크 모드'의 네 레슨에서 9문제를 내고, 9 · 10 · 14단원에서 3문제를 섞었습니다. 앞 단원 문제가 어느 레슨인지는 정답을 펼치면 보입니다. 값이나 고친 줄은 종이에 끝까지 쓴 뒤 정답을 펼치세요. 막히면 정답 앞의 힌트를 먼저 펼칩니다. 점수는 매기지 않습니다.

결과를 묻는 문제의 결과 화면과 잰 값은 정답을 펼치면 보입니다. 결과 화면은 늘 마우스를 올리기 전의 밝은 화면으로 그려집니다 — 마우스를 올린 뒤 · 움직임 줄이기 · 어두운 화면에서의 값은 잰 값 칸으로 봅니다. 컴퓨터가 있으면 문제의 HTML과 CSS를 파일로 저장해 브라우저로 열고, 개발자 도구 Rendering의 흉내(움직임 줄이기 · 어두운 화면)를 켜서 Computed 창의 값을 맞춰 봅니다. 결과는 모두 크롬 기준입니다.

이 단원을 이미 안다면 1 · 2 · 3 · 6 · 7번을 먼저 풀어 보세요. 건너뛸지 볼 때는 힌트를 펴지 않고 풉니다. 다 맞히면 이 단원은 건너뛰어도 됩니다. 틀린 문제와 힌트를 펴고 맞힌 문제는 정답 끝의 '다시 볼 레슨'으로 갑니다. 끝의 두 문제(11 · 12번)는 직접 만들어 보는 문제라, 건너뛸지 가릴 때는 풀지 않아도 됩니다. 10번까지 풀고 쉬었다가 11번부터 이어 풀어도 됩니다. ⟨컴퓨터⟩가 붙은 문제는 컴퓨터에서 파일을 만들어 풉니다. 폰으로만 본다면 그 문제 끝의 폰 물음 하나로 대신합니다.

문제 1

사진 동아리 공지의 '가입 안내' 링크입니다. 마우스를 올리면 글자색이 빨강으로 바뀝니다. (가) 마우스를 올린 링크 (나) 마우스를 뗀 링크 — 각각 Computed 창의 transition-duration은 몇 초일까요? 숫자만 쓰세요.

<a href="join.html">가입 안내</a>
a {
  color: #005bb5;
}

a:hover {
  color: #d70015;
  transition: color 300ms;
}
힌트

마우스를 올린 뒤와 뗀 뒤에 링크에 닿는 규칙을 각각 적어 보세요.

힌트 둘

이어 그리는 시간은 값이 바뀐 뒤 그 요소에 닿는 transition에서 옵니다.

정답

(가) 0.3 · (나) 0

마우스를 뗀 링크에는 a 규칙만 닿습니다.

결과 화면
잰 값개발자 도구에서 본 값
  • a의 transition-duration0s

마우스를 올린 링크에는 a:hover 규칙까지 닿습니다.

<a href="join.html">
  가입 안내
</a>
a {
  color: #005bb5;
}

a:hover {
  color: #d70015;
  transition: color 300ms;
}
결과 화면
잰 값개발자 도구에서 본 값
  • a의 transition-duration0.3s
  • a의 글자색rgb(215, 0, 21)
  • 마우스를 올릴 때는 :hover까지 닿은 상태가 '바뀐 뒤'라 0.3초 동안 빨강으로 이어집니다. 뗄 때는 기본 규칙만 닿은 상태가 '바뀐 뒤'인데, 그 규칙에 전환이 없어 처음 값 0초 — 파랑으로 툭 돌아옵니다.
  • (나)를 0.3이라고 썼다면 한 번 닿은 전환이 남는다고 본 것입니다. 뗄 때도 이어지게 하려면 transition 줄을 a 규칙으로 옮깁니다.
  • 0.3s · 300ms로 써도 같은 답입니다.

다시 볼 레슨: transition

문제 2

플로우 카페 메뉴판의 메뉴 칸입니다. 마우스를 올리면 테두리 색과 바탕색이 바뀝니다. 두 색은 어떻게 바뀔까요? 움직임이라 그림 대신 글 보기에서 고르세요.

<p class="menu">
  레몬에이드 5000
</p>
.menu {
  border: 2px solid #d2d2d7;
  transition:
    color 300ms ease,
    border-color 300ms ease;
}

.menu:hover {
  border-color: #005bb5;
  background-color: #e8f3ff;
}
결과 화면
  • ① 테두리 색과 바탕색이 함께 0.3초 동안 이어 바뀐다
  • ② 테두리 색과 바탕색이 함께 한순간에 바뀐다
  • ③ 테두리 색은 0.3초 동안 이어 바뀌고, 바탕색은 한순간에 바뀐다
힌트

:hover 규칙이 바꾸는 속성을 하나씩 적어 보세요.

힌트 둘

적은 속성을 transition의 목록과 견줘 보세요. 목록에 없는 속성은 바뀌는 과정을 정한 선언이 없습니다.

정답

③입니다. 아래 잰 값은 마우스를 올리고 전환이 끝난 뒤의 값입니다.

<p class="menu">
  레몬에이드 5000
</p>
.menu {
  border: 2px solid #d2d2d7;
  transition:
    color 300ms ease,
    border-color 300ms ease;
}

.menu:hover {
  border-color: #005bb5;
  background-color: #e8f3ff;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .menu의 transition-propertycolor, border-color
  • .menu의 border-top-colorrgb(0, 91, 181)
  • .menu의 background-colorrgb(232, 243, 255)
  • 전환은 적은 속성의 값이 바뀔 때만 이어 그립니다. border-color는 목록에 있어 0.3초 동안 이어지고, background-color는 목록에 없어 한순간에 바뀝니다. color는 글자색이라 바탕색과 다른 속성이고, 이 칸에서는 글자색이 바뀌지 않아 하는 일이 없습니다.
  • ①은 transition을 적으면 바뀌는 값이 모두 이어진다고, 또는 color가 바탕색까지 정한다고 본 것입니다. 바탕색도 잇고 싶으면 목록의 color를 background-color로 바꿉니다.
  • ②는 기본 규칙의 전환은 마우스를 뗄 때만 쓰인다고 본 것입니다. 기본 규칙은 :hover 상태에도 그대로 닿아, 올릴 때와 뗄 때 모두 쓰입니다.

다시 볼 레슨: transition

문제 3

여행 일정 페이지의 날짜 줄입니다. 짝마다 두 코드의 화면이 같은지 다른지 쓰세요. 다르면 어디가 다른지도 한 줄로 쓰세요. 셋 모두 같은 답이 나와도 됩니다.

(가)

<p>1일 <span>2일</span> 3일</p>
span {
  display: inline-block;
  background-color: #e8f3ff;
  transform: translateX(12px);
}
<p>1일 <span>2일</span> 3일</p>
span {
  display: inline-block;
  background-color: #e8f3ff;
  position: relative;
  left: 12px;
}

(나)

<p>1일 <span>2일</span> 3일</p>
span {
  display: inline-block;
  background-color: #e8f3ff;
  transform: translateX(12px);
}
<p>1일 <span>2일</span> 3일</p>
span {
  display: inline-block;
  background-color: #e8f3ff;
  margin-left: 12px;
}

(다)

<p>1일 <span>2일</span> 3일</p>
span {
  display: inline-block;
  background-color: #e8f3ff;
  transform: scale(1.5);
}
<p>1일 <span>2일</span> 3일</p>
span {
  display: inline-block;
  background-color: #e8f3ff;
  font-size: 1.5em;
}
힌트

짝마다 두 코드에서 다른 줄을 찾고, '2일'과 '3일'이 어디에 놓일지 종이에 그려 보세요.

힌트 둘

짝마다 다른 줄이 배치를 바꾸는지, 그리기만 바꾸는지 나눠 보세요. transform은 배치가 끝난 뒤 그려진 모습만 바꾸고, relative 상자는 옮겨 놓아도 원래 자리를 그대로 차지합니다.

정답

(가) 같다 · (나) 다르다 — 뒤 코드는 '3일'이 오른쪽으로 밀리고 '2일' 앞 틈이 넓다 · (다) 다르다 — 뒤 코드는 줄이 높아지고 '3일'이 오른쪽으로 밀린다

결과 화면(가) 첫째 코드
결과 화면(가) 둘째 코드
결과 화면(나) 첫째 코드
결과 화면(나) 둘째 코드
결과 화면(다) 첫째 코드
결과 화면(다) 둘째 코드
  • (가) 두 줄 모두 '2일'의 원래 자리를 남긴 채 그려진 모습만 12px 오른쪽에 둡니다. '3일'은 원래 자리를 보고 놓여 그대로이고, 옮긴 '2일'이 '3일' 앞쪽에 겹칩니다(브라우저 규칙 8).
  • (나) 바깥 여백은 배치의 일이라 '2일'과 함께 '3일'도 12px 밀립니다. 겹치지 않습니다.
  • (다) scale(1.5)는 그려진 크기만 키워 이웃 자리는 그대로이고 '2일'이 양옆 글에 겹칩니다. 글자 크기를 키우면 배치가 다시 되어 줄이 높아지고 '3일'이 밀립니다.
  • (가)를 다르다고 했다면 transform이 상자를 옮겨 이웃이 비켜 준다고 본 것입니다(transform이 배치라는 생각). (나)를 같다고 한 것도 같은 생각입니다.
  • (다)를 같다고 했다면 scale()이 글자를 키우는 것과 같다고 본 것입니다. 배율로 그리기만 할 뿐 배치는 그대로입니다.

다시 볼 레슨: transform으로 옮기고 키우기 · 일반 흐름과 position

문제 4

여행 일정 페이지의 사진 칸을 0.75배로 줄여 그렸습니다. (가) 사진 칸의 너비(그려진 너비 — 테두리까지) (나) 배치 너비는 각각 몇 px일까요? 숫자만 쓰세요.

<div class="photo">바다 사진</div>
.photo {
  width: 200px;
  padding: 10px;
  border: 2px solid #d2d2d7;
  transform: scale(0.75);
}
힌트

먼저 transform이 없을 때 이 칸이 차지하는 너비를 상자 셈으로 적어 보세요.

정답

(가) 168 · (나) 224

결과 화면
잰 값개발자 도구에서 본 값
  • .photo의 너비168px
  • .photo의 배치 너비224px
  • .photo의 width200px
걸음셈값
1. 내용 칸width200
2. 안쪽 여백 더하기200 + 10 × 2220
3. 테두리 더하기220 + 2 × 2224
4. 바깥 여백은 따로없음—
  • 배치 너비는 상자 셈 그대로 224입니다(브라우저 규칙 6). scale(0.75)는 배치가 끝난 뒤 그 상자를 0.75배로 그려 224 × 0.75 = 168입니다(브라우저 규칙 8).
  • 150 · 200이라고 썼다면 width가 차지하는 너비라고 본 것입니다(width가 차지하는 너비라는 생각).
  • (나)를 168이라고 썼다면 scale()이 배치 크기도 줄인다고 본 것입니다(transform이 배치라는 생각). 아래 요소는 224 너비의 원래 자리를 보고 놓입니다.

다시 볼 레슨: 너비와 box-sizing · transform으로 옮기고 키우기

문제 5

플로우 카페 메뉴판의 메뉴 줄입니다. '추천' 표시를 띄우려고 absolute와 top: 0만 줬습니다. 메뉴 줄에도, 그 바깥 어느 요소에도 position을 주지 않았습니다. 화면에 어떻게 보일까요? 보기 셋 가운데 고르세요.

<p class="menu">
  바닐라 라테 <b>추천</b>
</p>
b {
  position: absolute;
  top: 0;
}
힌트

'추천'부터 바깥으로 p · body · html을 나무로 적고, 요소마다 준 position을 옆에 써 보세요.

힌트 둘

기준 상자는 부모가 아니라 바깥으로 찾은 첫 position 조상이고, 없으면 페이지입니다. 적은 칸(top)과 적지 않은 칸(left)도 나눠 보세요.

정답

① — '추천'이 메뉴 이름 뒤에서 페이지 맨 위로 올라가 있습니다.

결과 화면
  • 기준 상자는 나무를 따라 바깥으로 찾은 첫 position 조상입니다. p · body · html 어디에도 없어 페이지가 기준 상자이고, top: 0은 페이지 맨 위입니다(브라우저 규칙 8).
  • left를 적지 않아 가로는 원래 있던 자리 — 메뉴 이름 바로 뒤 — 그대로입니다.
  • ②는 부모 p를 기준 상자로 본 것입니다(부모 기준이라는 생각). 부모가 기준 상자가 되려면 부모에 position: relative를 줍니다.
  • ③은 적지 않은 left도 0이 된다고 본 것입니다. top만 적은 상자는 가로로 원래 자리에 떠 있습니다.

다시 볼 레슨: 기준 상자와 absolute · 일반 흐름과 position

문제 6

레시피 노트 김치볶음밥 페이지의 큰 그림은 마우스를 올리면 1.05배로 커집니다. 움직임 줄이기를 켠 사람에게는 커지지 않게 하려 합니다. ____ 자리에 조건 블록 안에 들어갈 규칙을 쓰세요.

<img
  class="dish"
  src="images/kimchi.svg"
  alt="김치볶음밥"
  width="200"
  height="150"
>
.dish {
  transition:
    transform 200ms ease;
}

.dish:hover {
  transform: scale(1.05);
}

@media (
  prefers-reduced-motion: reduce
) {
  ____
}

나와야 할 결과는 둘입니다.

  • 움직임 줄이기를 켜고 그림에 마우스를 올리면 그림 너비가 200px 그대로다.
  • 움직임 줄이기를 켜지 않은 브라우저에서는 마우스를 올리면 그림이 210px로 커진다.
힌트

움직임 줄이기에서 마우스를 올린 그림에 닿는 선언을 기본 규칙 · :hover 규칙 · 조건 블록 순으로 적어 보세요.

힌트 둘

transition: none은 바뀌는 과정만 없앱니다. 끝 모양인 transform은 그대로 닿습니다.

정답

모범 답입니다 — 레슨의 꼴대로 전환과 변형을 둘 다 끕니다.

@media (
  prefers-reduced-motion
) {
  .dish {
    transition: none;
  }

  .dish:hover {
    transform: none;
  }
}

첫 결과 — 움직임 줄이기를 켜고 마우스를 올린 뒤입니다. 결과 화면은 앞 CSS만 그린 것과 조건 블록까지 그린 것이 차례로 나옵니다.

<img
  class="dish"
  src="images/kimchi.svg"
  alt="김치볶음밥"
  width="200"
  height="150"
>
.dish {
  transition:
    transform 200ms ease;
}

.dish:hover {
  transform: scale(1.05);
}
결과 화면
@media (
  prefers-reduced-motion
) {
  .dish {
    transition: none;
  }

  .dish:hover {
    transform: none;
  }
}
결과 화면
잰 값개발자 도구에서 본 값
  • .dish의 너비200px
  • .dish의 transition-duration0s

둘째 결과 — 같은 코드를 움직임 줄이기 없이 마우스를 올린 뒤입니다.

<img
  class="dish"
  src="images/kimchi.svg"
  alt="김치볶음밥"
  width="200"
  height="150"
>
.dish {
  transition:
    transform 200ms ease;
}

.dish:hover {
  transform: scale(1.05);
}
결과 화면
@media (
  prefers-reduced-motion
) {
  .dish {
    transition: none;
  }

  .dish:hover {
    transform: none;
  }
}
결과 화면
잰 값개발자 도구에서 본 값
  • .dish의 너비210px

정답의 코드는 줄 폭 때문에 : reduce를 뺀 꼴로 적었습니다 — 같은 뜻입니다.

다르게 써도 되는 곳:

  • .dish:hover 규칙에 transform: none 하나만 써도 결과 둘이 맞습니다 — 끝 모양이 그대로면 이어 그릴 것도 없습니다. 레슨의 꼴처럼 전환도 함께 끄면 뒤에 :hover에 다른 값을 더해도 움직임 줄이기에서 이어 그리지 않습니다.
  • transform: none 대신 transform: scale(1)도 원래 크기입니다.

흔한 잘못:

  • .dish { transition: none; }⁠만 쓰면 첫 결과에서 그림이 한순간에 210px로 커집니다 — 전환만 끄면 순간 이동입니다.
  • 조건 블록을 기본 규칙 위로 옮기면 나중에 쓴 .dish:hover가 이겨 그대로 커집니다.

다시 볼 레슨: 움직임 줄이기

문제 7

플로우 카페 후기 카드입니다. 어두운 화면 조건 블록에서 면 색 변수만 바꿨습니다. 어두운 화면으로 설정한 브라우저에서 카드와 후기 칸은 어떻게 보일까요? 어두운 화면은 결과 화면이 그리지 않아 글 보기에서 고릅니다.

<div class="card">
  <textarea>또 올게요</textarea>
</div>
:root {
  --surface: #ffffff;
}

.card {
  padding: 16px;
  background: var(--surface);
}
@media (
  prefers-color-scheme: dark
) {
  :root {
    --surface: #1d2633;
  }
}
  • ① 카드도 후기 칸도 밝은 채다
  • ② 카드는 어두운 남색이고, 후기 칸은 흰 채다
  • ③ 카드도 후기 칸도 어둡다
힌트

어두운 화면에서 --surface의 값을 먼저 정하고, 카드와 후기 칸이 각각 무엇으로 칠해지는지 적어 보세요.

힌트 둘

입력 칸 · 후기 칸은 브라우저가 기본 모양으로 그립니다. 그 모양을 어두운 화면에 맞추는 한 줄이 이 코드에 있는지 보세요.

정답

②입니다. 결과 화면은 밝은 화면으로 그려집니다 — 첫째는 앞 CSS만, 둘째는 조건 블록까지 그린 것이고, 아래 잰 값이 어두운 화면에서 잰 값입니다.

결과 화면
결과 화면
잰 값개발자 도구에서 본 값
  • .card의 background-colorrgb(29, 38, 51)
  • textarea의 background-colorrgb(255, 255, 255)
  • 조건이 맞아 --surface가 #1d2633이 되고, var(--surface)로 칠한 카드는 규칙을 한 글자도 바꾸지 않았는데 함께 어두워집니다. var()는 지금의 변수 값을 꺼냅니다.
  • 후기 칸은 우리가 칠하지 않아 브라우저가 기본 모양으로 그립니다. :root에 color-scheme: light dark⁠가 없어 어두운 화면에서도 흰 칸입니다.
  • ①은 var()가 처음에 꺼낸 값을 한 번 베껴 둔다고 본 것입니다.
  • ③은 변수를 바꾸면 브라우저가 그리는 칸까지 바뀐다고 본 것입니다. 칸은 color-scheme: light dark⁠를 더해야 어두워집니다(크롬에서는 rgb(59, 59, 59) — 칸 모양은 브라우저마다 다릅니다).

다시 볼 레슨: 변수만 바꾸는 다크 모드

문제 8

사진 동아리 공지의 '찾아오는 길' 링크입니다. 어두운 화면에서 읽히게 밝은 파랑을 조건 블록에 따로 적었습니다. 어두운 화면으로 설정한 브라우저에서 '찾아오는 길'의 글자색은 무엇일까요? 코드에 적힌 꼴로 쓰세요.

<p class="notice">
  모임 장소
  <a href="place.html">
    찾아오는 길
  </a>
</p>
.notice a {
  color: #005bb5;
}

@media (
  prefers-color-scheme: dark
) {
  a {
    color: #9ac7ff;
  }
}
힌트

어두운 화면에서 '찾아오는 길'에 닿는 color 선언 둘을 손 점수판에 적어 보세요.

힌트 둘

조건 블록은 그 안 규칙이 닿을지만 정합니다. 닿은 뒤에는 다른 규칙과 같은 차례로 겨룹니다.

정답

받는 답: #005bb5 — 어두운 화면에서도 짙은 파랑 그대로입니다.

결과 화면
잰 값개발자 도구에서 본 값
  • a의 글자색rgb(0, 91, 181)
속성선언출처명시도차례이긴 값
color.notice a · #005bb5우리 CSS0-1-11#005bb5
a · #9ac7ff우리 CSS0-0-12
  • 어두운 화면에서는 조건이 맞아 블록 안 a 규칙도 닿습니다. 그래도 출처가 같아 명시도로 가르고, 클래스 칸에서 .notice a가 이깁니다. 조건 블록은 명시도를 올리지 않아 차례 칸까지 가지 않습니다(브라우저 규칙 5).
  • #9ac7ff라고 썼다면 나중에 쓴 쪽이 늘 이긴다고 본 것입니다(뒤가 늘 이긴다는 생각).
  • rgb(0, 91, 181) · rgb(0 91 181)도 같은 답입니다.
  • 링크 색을 var(--accent)로 꺼내 쓰고 조건 블록에서 :root의 변수 값만 바꾸면 이런 겨룸이 생기지 않습니다 — 다크 모드 틀의 하위 목표 2입니다.

다시 볼 레슨: 명시도 · 변수만 바꾸는 다크 모드

문제 9

하고 싶은 일마다 쓸 이름을 떠올려 쓰세요.

  • (가) 값이 바뀔 때 앞 값에서 새 값까지를 정한 시간 동안 이어 그리게 하는 CSS 속성
  • (나) 요소의 그려진 모습을 옮기거나 키우되, 이웃 요소의 자리는 그대로 두는 CSS 속성
  • (다) 사용자가 운영체제의 접근성 설정에서 화면 효과를 줄여 달라고 골랐는지 읽는 미디어 쿼리 조건
  • (라) 페이지가 밝은 화면과 어두운 화면을 모두 지원한다고 알려, 입력 칸 같은 브라우저가 그리는 모양을 맞추게 하는 CSS 속성
힌트

(가) · (나)는 영어 낱말 하나이고, (다) · (라)는 낱말 둘 · 셋을 하이픈(-)으로 이은 이름입니다.

정답

받는 답: (가) transition · (나) transform · (다) prefers-reduced-motion · (라) color-scheme

  • (다)는 @media (prefers-reduced-motion: reduce)⁠처럼 값 reduce까지 쓴 꼴도 같은 답입니다.
  • (가)와 (나)를 바꿔 쓰기 쉽습니다. transition은 두 상태 사이를 잇는 과정, transform은 그려진 모습 자체를 바꾸는 일입니다.
  • (라)를 prefers-color-scheme이라고 썼다면 어두운 화면을 골랐는지 읽는 조건과 헷갈린 것입니다. 조건은 닿을 규칙을 고르고, color-scheme은 브라우저가 그리는 칸 모양을 맞춥니다.

다시 볼 레슨: transition · transform으로 옮기고 키우기 · 움직임 줄이기 · 변수만 바꾸는 다크 모드

문제 10

여행 일정 페이지의 '오늘' 도장을 오른쪽으로 8px, 위로 4px 옮겨 그리려고 두 줄을 썼습니다. 그런데 도장이 위로만 옮겨지고 오른쪽으로는 가지 않았습니다. 개발자 도구에서 도장을 골라 보니 Styles 창의 .stamp 규칙에서 transform: translateX(8px) 줄에 취소선이 그어져 있고, 경고 표시는 없습니다. 이 페이지는 CSS를 HTML 안 style 요소에 썼습니다. 고칠 줄을 골라 고쳐 보세요.

힌트

찾는 차례대로 1번부터 지워 가세요. 취소선은 그 선언이 무엇에 졌다는 표시인지 떠올립니다.

힌트 둘

한 속성에 선언이 둘이면 나중 것이 이깁니다. 옮기기 둘을 함께 하려면 한 선언에 이어 적습니다.

결과 화면일부러 틀린 코드

여기서 고친 코드는 저장하지도 보내지도 않습니다. 새로 열면 처음 코드입니다.

정답

받는 답: 7번 줄의 translateY(-4px) 앞에 translateX(8px)을 더해, 두 함수를 한 transform 선언에 잇습니다. 고친 style 요소 안 규칙을 떼어 그리면 이렇습니다.

<div class="stamp">오늘</div>
.stamp {
  width: 64px;
  height: 32px;
  background: #ffe0e0;
  transform:
    translateX(8px)
    translateY(-4px);
}
결과 화면
잰 값개발자 도구에서 본 값
  • .stamp의 왼쪽 자리16px
  • .stamp의 위 자리4px
  • .stamp의 배치 위 자리8px

찾는 차례 4번에서 찾습니다.

  • 1번 — 도장이 위로 4px 옮겨졌으니 규칙은 닿았습니다.
  • 2번 — Elements 패널에 div.stamp가 있습니다.
  • 3번 — Styles 창에 .stamp 규칙이 보입니다.
  • 4번 — 경고 표시가 없으니 두 줄 모두 알아들었습니다. 이겼나를 봅니다. translateX(8px) 줄의 취소선은 같은 속성 transform의 다른 선언에 졌다는 표시입니다. 같은 규칙 안이라 나중에 쓴 7번 줄이 이겨 위로만 옮깁니다(브라우저 규칙 5).

두 선언은 합쳐지지 않습니다. 옮기기 둘을 한 선언에 빈칸으로 이어 적으면 둘 다 그려집니다. 도장은 원래 자리(왼쪽 8 · 위 8 — body의 바깥 여백)를 남긴 채 오른쪽으로 8 · 위로 4 옮겨 그려집니다(브라우저 규칙 8).

다르게 고쳐도 되는 곳:

  • 6번 줄을 두 함수를 이은 선언으로 고치고 7번 줄을 지워도 같습니다 — 줄 둘을 고쳐야 해서 받는 답으로 두지 않았습니다.

  • 두 함수의 차례를 바꿔 translateY()를 먼저 적어도 같은 자리입니다 — 옮기기끼리는 차례가 결과를 바꾸지 않습니다.

  • 7번 줄을 고친 뒤 6번 줄은 지는 줄로 남습니다. 지워 두면 Styles 창이 깔끔합니다.

  • 흔한 오답은 6번 줄에 !important를 붙이는 것입니다. 그러면 오른쪽으로만 옮겨지고 위로는 가지 않습니다 — 이긴 선언이 바뀔 뿐 둘이 합쳐지지 않습니다.

  • 6번 · 7번 줄의 차례만 바꾸는 것도 흔합니다. 이번에는 오른쪽으로만 옮겨집니다.

다시 볼 레슨: transform으로 옮기고 키우기 · 나중에 쓴 규칙

문제 11 (만들어 보기)

플로우 카페 주문 폼의 CSS입니다. 하위 목표 1(색을 변수로 모으기)은 끝냈습니다.

<form class="order">
  <label for="memo">
    주문 메모
  </label>
  <input id="memo">
</form>
/* 1. 색을 변수로 모으기 */
:root {
  --surface: #ffffff;
  --text: #1d1d1f;
  --line: #d2d2d7;
}

.order {
  padding: 16px;
  border:
    1px solid var(--line);
  color: var(--text);
  background-color:
    var(--surface);
}
결과 화면

다크 모드 틀의 나머지 하위 목표 둘을 … 자리에 쓰세요. 어두운 값은 --surface #1d2633 · --text #f1f4f8 · --line #3a4552입니다.

/* 2. 어두운 값 */
…

/* 3. color-scheme */
…

나와야 할 결과는 둘입니다.

  • 밝은 화면에서는 폼이 채우기 전과 같다 — 흰 바탕 · 짙은 글자 · 회색 선.
  • 어두운 화면에서는 폼 바탕 · 글자 · 선이 어두운 값으로 바뀌고, 주문 메모 칸도 어두운 칸 모양으로 그려진다.
힌트

하위 목표 2는 조건 블록 안에 어느 선택자를 두는지, 3은 어느 요소에 한 번 두면 모든 칸이 따르는지 먼저 정하세요.

정답

모범 답입니다.

/* 2. 어두운 값 */
@media (
  prefers-color-scheme: dark
) {
  :root {
    --surface: #1d2633;
    --text: #f1f4f8;
    --line: #3a4552;
  }
}

/* 3. color-scheme */
:root {
  color-scheme: light dark;
}

첫 결과 — 밝은 화면입니다.

<form class="order">
  <label for="memo">
    주문 메모
  </label>
  <input id="memo">
</form>
:root {
  --surface: #ffffff;
  --text: #1d1d1f;
  --line: #d2d2d7;
}

.order {
  padding: 16px;
  border:
    1px solid var(--line);
  color: var(--text);
  background-color:
    var(--surface);
}
결과 화면
@media (
  prefers-color-scheme: dark
) {
  :root {
    --surface: #1d2633;
    --text: #f1f4f8;
    --line: #3a4552;
  }
}

:root {
  color-scheme: light dark;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .order의 background-colorrgb(255, 255, 255)
  • .order의 글자색rgb(29, 29, 31)
  • .order의 border-top-colorrgb(210, 210, 215)

둘째 결과 — 같은 코드를 어두운 화면에서 잰 값입니다. 결과 화면은 밝은 화면으로 그려집니다.

<form class="order">
  <label for="memo">
    주문 메모
  </label>
  <input id="memo">
</form>
:root {
  --surface: #ffffff;
  --text: #1d1d1f;
  --line: #d2d2d7;
}

.order {
  padding: 16px;
  border:
    1px solid var(--line);
  color: var(--text);
  background-color:
    var(--surface);
}
결과 화면
@media (
  prefers-color-scheme: dark
) {
  :root {
    --surface: #1d2633;
    --text: #f1f4f8;
    --line: #3a4552;
  }
}

:root {
  color-scheme: light dark;
}
결과 화면
잰 값개발자 도구에서 본 값
  • .order의 background-colorrgb(29, 38, 51)
  • .order의 글자색rgb(241, 244, 248)
  • .order의 border-top-colorrgb(58, 69, 82)
  • #memo의 color-schemelight dark
  • #memo의 background-colorrgb(59, 59, 59)

주문 메모 칸의 rgb(59, 59, 59)는 크롬이 그린 어두운 칸 모양입니다 — 칸 모양은 브라우저마다 다르니, 어두운 칸으로 그려졌는지는 color-scheme이 light dark인지로 봅니다.

확인한 결과가 둘 다 맞으면 맞습니다. 코드가 모범 답과 달라도 됩니다.

다르게 써도 되는 곳:

  • color-scheme: light dark;⁠를 하위 목표 1의 :root 끝에 넣어도 같습니다.
  • 조건 블록 안 세 줄의 차례는 바꿔도 됩니다.
  • prefers-color-scheme: dark⁠를 한 줄에 써도 같습니다 — 줄 나누기는 줄 폭 때문입니다.

흔한 잘못:

  • 조건 블록 안에 .order 규칙을 다시 적고 어두운 색을 값으로 쓰면, 이 폼은 어두워져도 다음에 변수를 쓰는 곳을 더할 때마다 블록에 또 적어야 합니다.
  • 조건 블록을 하위 목표 1의 :root 위에 두면 나중에 쓴 밝은 값이 이겨 어두운 화면에서도 밝은 채입니다.
  • 하위 목표 3을 빠뜨리면 폼은 어두워지는데 주문 메모 칸만 흰 채로 남습니다.

다시 볼 레슨: 변수만 바꾸는 다크 모드

문제 12 (만들어 보기)

레시피 노트의 열다섯째 단계입니다(⟨컴퓨터⟩ · 25분 안팎 · 걸음마다 멈춰도 됩니다). 카드에 마우스를 올리면 살짝 떠오르게 하고, 움직임 줄이기 설정에서는 떠오르지 않게 하고, 어두운 화면 설정에서는 변수 값만 바꿔 어두워지게 합니다. 레시피 노트는 이 단계에서 끝납니다. 15단원 연습 문제의 만들어 보기를 마쳤다면 그 폴더에서 이어 갑니다.

이 단계 시작 파일 — 15단원 끝 상태

recipe-note 폴더에 세 파일을 받아 둡니다.

recipe-note 안에 images 폴더를 만들고 그림 여섯을 받아 둡니다. 앞 단계에서 받았으면 그대로 씁니다.

할 일 — 걸음 넷입니다. 이 단계는 style.css만 고칩니다.

걸음 1 — 카드 떠오르기. 레시피 목록의 카드에 마우스를 올리면 0.18초 동안 위로 4px 떠오르고, 마우스를 떼면 같은 시간 동안 내려오게 합니다. 이웃 카드는 움직이지 않아야 합니다.

걸음 2 — 움직임 줄이기. 운영체제에서 움직임 줄이기를 켠 사람에게는 카드가 떠오르지 않게 합니다. 카드의 글 · 링크 · 배지는 그대로 둡니다.

걸음 3 — 어두운 값. /* 색 */ 구역의 :root 바로 아래에 어두운 화면 조건 블록을 두고, 여섯 변수의 값만 바꿉니다. 카드 · 링크 · 머리글 규칙은 블록 안에 옮겨 적지 않습니다.

변수역할어두운 값
--bg페이지 바탕#121820
--surface바탕 위에 올라앉은 면#1d2633
--text글자#f1f4f8
--muted보조 글자#a3adb8
--line선#3a4552
--accent링크 · 강조 · 단추#9ac7ff

걸음 4 — 브라우저가 그리는 모양. 김치볶음밥 페이지 후기 폼의 라디오처럼 우리가 칠하지 않은 칸 모양이 어두운 화면에서 어두운 모양으로 그려지게 :root에 한 줄을 더합니다. 라디오 묶음(fieldset)의 테두리도 다른 선처럼 1px solid와 선 변수 색으로 다시 씁니다 — 기본 테두리는 두 색으로 그려지는 홈 모양이라, 색만 바꾸면 화면에서 선보다 밝게 보입니다.

확인 목록:

  • 목록 페이지 카드에 마우스를 올리면 0.18초 동안 떠오르고, 떼면 천천히 내려온다. 아래 · 옆 카드는 그대로다(화면 · 걸음 1).
  • 개발자 도구 Rendering에서 움직임 줄이기를 흉내 내고 카드에 마우스를 올리면 카드가 그 자리에 있다 — Computed 창의 transform이 none이다(Computed 창 · 걸음 2).
  • 어두운 화면을 흉내 내면 바탕 · 카드 · 글자 · 링크 · 머리글이 함께 어두운 값으로 바뀐다 — body의 background-color가 rgb(18, 24, 32)이다(Computed 창 · 걸음 3).
  • style.css에서 # 기호로 시작하는 색 값은 두 :root의 열두 줄에만 있다(편집기 · 걸음 3).
  • 어두운 화면에서 후기 폼의 라디오가 어두운 모양이고, 라디오 묶음의 테두리가 표 · 칸의 선과 같은 색 · 같은 굵기다(화면 · 걸음 4).
  • 확인이 끝나면 Rendering의 두 흉내를 No emulation으로 되돌린다(네 걸음 끝).

폰으로만 따라왔다면 이 물음 하나로 대신합니다. 걸음 2를 마친 레시피 노트를, 움직임 줄이기를 켠 사람이 보며 카드에 마우스를 올리면 카드는 어떻게 될까요?

  • ① 0.18초 동안 4px 떠오른다
  • ② 한순간에 4px 떠오른다
  • ③ 그 자리에 있다
힌트

걸음마다 그 걸음의 확인 목록을 봅니다. 걸음 2는 끌 것이 전환인지 떠오른 모양인지 둘 다인지 먼저 적고, 걸음 3은 조건 블록 안의 선택자부터 정합니다.

정답

모범 답입니다. 바뀐 곳만 싣습니다 — 다 고친 style.css는 아래 받기 파일과 같습니다(index.html · kimchi.html은 15단원 끝 상태 그대로). 정답의 코드는 줄 폭 때문에 : reduce를 뺀 꼴로 적었습니다 — 받기 파일에는 적혀 있고, 같은 뜻입니다.

/* 색 */
:root {
  …
  --accent: #005bb5;
  color-scheme: light dark;
}
@media (
  prefers-color-scheme: dark
) {
  :root {
    --bg: #121820;
    --surface: #1d2633;
    --text: #f1f4f8;
    --muted: #a3adb8;
    --line: #3a4552;
    --accent: #9ac7ff;
  }
}
.recipe-card {
  …
  transition:
    transform 180ms ease;
}

.recipe-card:hover {
  transform:
    translateY(-4px);
}
@media (
  prefers-reduced-motion
) {
  .recipe-card {
    transition: none;
  }

  .recipe-card:hover {
    transform: none;
  }
}
fieldset {
  border:
    1px solid var(--line);
}

카드 하나를 떼어 잰 값입니다. 받기 파일과 같은 선언만 골랐습니다. 결과 화면은 늘 밝은 화면 · 마우스를 올리기 전으로, 앞 CSS만 그린 것과 이어 쓴 CSS까지 그린 것이 차례로 나옵니다.

걸음 3 — 어두운 화면에서 잰 값입니다.

<div class="recipe-card">
  <h2>김치볶음밥</h2>
  <p>조리 15분</p>
</div>
:root {
  --bg: #f5f5f7;
  --surface: #ffffff;
}

body {
  background: var(--bg);
}

.recipe-card {
  background: var(--surface);
}
결과 화면
@media (
  prefers-color-scheme: dark
) {
  :root {
    --bg: #121820;
    --surface: #1d2633;
  }
}
결과 화면
잰 값개발자 도구에서 본 값
  • body의 background-colorrgb(18, 24, 32)
  • .recipe-card의 background-colorrgb(29, 38, 51)

걸음 2 — 움직임 줄이기에서 카드에 마우스를 올린 뒤 잰 값입니다.

<div class="recipe-card">
  김치볶음밥
</div>
.recipe-card {
  transition:
    transform 180ms ease;
}

.recipe-card:hover {
  transform:
    translateY(-4px);
}
결과 화면
@media (
  prefers-reduced-motion
) {
  .recipe-card {
    transition: none;
  }

  .recipe-card:hover {
    transform: none;
  }
}
결과 화면
잰 값개발자 도구에서 본 값
  • .recipe-card의 transformnone
  • .recipe-card의 transition-duration0s
  • .recipe-card의 위 자리8px

확인 목록을 다 채웠으면 맞습니다. 코드가 모범 답과 달라도 됩니다.

다르게 써도 되는 곳:

  • color-scheme: light dark;⁠는 따로 :root 규칙을 하나 더 두어 적어도 같습니다.
  • 움직임 줄이기 조건 블록은 .recipe-card:hover 규칙 뒤라면 파일 끝에 두어도 됩니다.
  • 떠오르는 시간은 0.15 ~ 0.25초 안이면 됩니다. 1초를 넘기면 기다리게 됩니다.

흔한 잘못:

  • 걸음 1에서 transition을 .recipe-card:hover에만 두면, 마우스를 뗄 때 카드가 툭 떨어집니다.
  • 걸음 1에서 margin-top: -4px로 올리면 아래 카드까지 함께 올라갑니다 — 격자 줄이 흔들립니다.
  • 걸음 2에서 transition: none만 두면 움직임 줄이기에서 카드가 한순간에 떠오릅니다.
  • 걸음 3의 조건 블록을 /* 색 */ 구역의 :root 위에 두면 나중에 쓴 밝은 값이 이겨 어두운 화면에서도 밝은 채입니다.
  • 걸음 4를 빠뜨리면 글자 칸 · 별점 칸은 변수로 칠해 어두워져도, 라디오는 밝은 모양으로 남습니다.
  • 걸음 4에서 fieldset에 border-color만 주면 계산된 색은 선 변수와 같아도, 기본 테두리가 두 색으로 그려지는 홈 모양이라 화면에서는 표 선보다 밝게 보입니다.

폰 물음의 답은 ③입니다. ②는 전환만 끈 꼴, ①은 조건 블록이 없는 꼴입니다.

다시 볼 레슨: transition · transform으로 옮기고 키우기 · 움직임 줄이기 · 변수만 바꾸는 다크 모드

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

HTML·CSS 목차로