Devin.KR

읽기와 포커스 표시

100분 안팎

학습 목표

확대와 키보드 사용에서도 콘텐츠를 읽을 수 있게 합니다.

개념

왜 확대와 키보드로 다시 확인하나요

작은 화면에서 겹치지 않는 것만으로 읽기와 조작이 완료되지는 않습니다. 글을 크게 보는 사용자는 버튼 문구가 두 줄이 되거나 안내가 길어지는 상황을 만납니다. 키보드 사용자는 현재 어느 조작에 있는지 알아야 합니다. 디자인을 마무리할 때 확대와 포커스 표시를 검사해 검색어 입력부터 상세와 돌아가기까지 같은 흐름을 유지합니다.

앞 모듈은 기본 폼 이름과 Tab 순서를 만들었고 앞 레슨은 가변 상자와 긴 콘텐츠를 처리했습니다. 이번 starter는 입력과 버튼의 높이를 2rem으로 고정하고 여백을 없앴습니다. 기본 포커스 규칙은 남겨 두었습니다. 기능을 삭제하는 과제가 아니라 내용이 커져도 조작 영역이 함께 늘어나고 포커스가 잘리지 않게 만드는 과제입니다.

읽는 크기와 상자 크기를 연결합니다

font:inherit으로 입력과 버튼이 주변 글꼴을 사용하게 합니다. 브라우저 기본 폼 글꼴이 본문과 다르면 같은 화면에서 읽기 크기가 달라질 수 있습니다. 본문의 line-height:1.6은 단위 없는 값이므로 해당 요소 글꼴 크기에 비례한 줄 높이를 사용합니다. 고정 px 줄 높이를 작은 값으로 두면 큰 글자가 잘리거나 이웃 줄과 겹칠 수 있습니다.

고정 height와 최소 높이는 다릅니다. min-height:2.75rem은 최소 공간을 확보하면서 내용이 많으면 더 커질 수 있게 합니다. padding:.625rem은 글자와 테두리 사이 여백을 제공합니다. 이 값은 실습의 표현 규칙이며 모든 접근성 기준을 만족했다는 인증이 아닙니다. 실제 글자 크기와 클릭 영역, 주변 간격을 함께 확인합니다.

button의 white-space:normal은 문구가 길어질 때 여러 줄을 허용합니다. max-width:100%는 부모 공간을 넘지 않게 하는 보조 규칙입니다. height를 남겨 두고 여러 줄만 허용하면 두 번째 줄이 잘릴 수 있습니다. starter의 고정 높이를 제거하고 최소 높이로 바꿉니다. 작은 글꼴로 되돌려 숨기는 방식은 확대 사용자의 목적에 맞지 않습니다.

포커스는 어디에 있는지를 알립니다

:focus-visible은 브라우저가 포커스 표시가 필요하다고 판단하는 상황에 사용하는 선택자입니다. 키보드로 이동한 링크, 검색 입력, select, 버튼을 직접 확인합니다. 마우스 클릭 때 같은 표시가 항상 나타날 것이라고 단정하지 않습니다. 운영체제와 브라우저의 판단이 있으므로 실제 키보드 조작과 함께 기록합니다.

outline:3px solid와 outline-offset:3px로 요소 둘레에 구분되는 표시를 둡니다. outline은 일반적인 박스 크기 계산에서 공간을 차지하는 border와 다릅니다. 부모가 overflow:hidden이면 바깥 외곽선이 잘릴 수 있으므로 색만 지정하고 끝내지 않습니다. 카드 끝 링크와 화면 가장자리 검색 버튼까지 이동하며 외곽선 전체를 확인합니다.

링크에는 밑줄을 남깁니다. 텍스트 색상만으로 조작 가능 여부를 전달하면 색 구분이 어려운 환경에서 식별하기 어렵습니다. 포커스가 보인다고 글자와 배경의 대비, 스크린 리더 발화, 모든 접근성이 보장되지는 않습니다. 이번 과제의 범위는 실제 읽기와 검색 조작 흐름이며 별도의 품질 모듈에서 사용자 흐름 테스트를 넓힙니다.

실제 확대 절차

먼저 일반 브라우저 창에서 index.html을 열고 확대 배율 100%와 CSS 뷰포트 폭을 기록합니다. 브라우저 메뉴의 확대를 200%로 바꿉니다. CSS transform:scale(2)는 화면 그림만 확대하며 같은 레이아웃 재배치를 보장하지 않습니다. 개발자 도구의 장치 배율이나 DPR 설정도 브라우저 페이지 확대와 구분합니다.

200%에서 검색 안내를 끝까지 읽고 검색어에 책을 입력합니다. 지역을 서울로 고른 뒤 Enter 제출과 버튼 제출을 각각 확인합니다. 실제 제출 주소의 q와 region을 확인하고 뒤로 돌아옵니다. 빈 검색어를 제출해 기본 검증 안내에 접근 가능한지도 봅니다. 목록 카드의 상세 링크로 이동하고 상세에서 돌아가기까지 확인합니다.

창의 크기를 유지한 채 확대하면 화면에 담기는 CSS 공간이 줄어 배치가 달라질 수 있습니다. 확대 전후 document.documentElement.clientWidth를 기록해 관찰 조건을 남깁니다. 320px 검사와 200% 확대는 각각 확인합니다. 이 두 점검만으로 WCAG 전체나 모든 확대 조건을 통과했다고 주장하지 않습니다.

키보드 점검과 오류 읽기

Tab으로 주요 메뉴에서 검색어, 지역, 검색 버튼, 각 상세 링크까지 이동합니다. Shift+Tab으로 역순도 확인합니다. OS와 브라우저에 따라 링크 키보드 탐색 설정이 필요할 수 있어 환경 설정을 적습니다. 마우스로 포커스를 찍은 뒤 표시가 없다는 이유만으로 :focus-visible이 잘못됐다고 판단하지 않습니다. 실제 키보드 이동에서 판단합니다.

양수 tabindex나 CSS order로 눈에 보이는 순서만 바꾸면 읽는 순서와 조작 순서가 어긋날 수 있습니다. 앞 모듈의 DOM 순서와 표준 input, select, button을 유지합니다. 포커스가 건너뛰는 경우 요소가 disabled인지, 숨겨졌는지, 실제 문서 순서가 어떤지 봅니다. 이번 단계에서는 JavaScript로 포커스를 강제로 이동하지 않습니다.

FAIL 높이 고정과 포커스 제거 없음은 starter 끝의 고정 height가 남았거나 outline을 없앴는지 확인하라는 단서입니다. FAIL 조작 영역 여백과 최소 높이는 제공 계약의 padding과 min-height를 읽습니다. CSS에서 잘못된 값은 브라우저가 무시할 수 있으므로 콘솔에 오류가 없어도 computed 속성을 확인합니다. 환경 오류와 표현 규칙 실패를 구분합니다.

검증 근거와 인계

포커스가 움직일 때 페이지가 스크롤돼 요소가 화면 안에 들어오는지도 확인합니다. 외곽선 색과 두께가 맞아도 고정된 헤더가 요소를 덮으면 현재 위치를 찾기 어렵습니다. 입력 중인 검색어, select의 현재 선택값, 버튼 문구를 읽을 수 있는 상태에서 다음 요소로 이동해야 합니다. 역방향 이동에서도 같은 요소가 보이는지 비교하면 특정 위치에서만 생기는 가림을 찾을 수 있습니다.

확대 후 버튼 문구가 여러 줄이 되면 텍스트의 높이와 위아래 padding이 함께 필요한 공간을 결정합니다. min-height는 그 합보다 작은 높이를 강제하지 않으므로 내용이 늘어나면 상자도 늘어날 수 있습니다. 다만 이전 규칙의 height나 max-height가 남아 있으면 성장에 제약이 생깁니다. computed에서 세 속성을 함께 읽고 버튼 아래의 요소가 자연스럽게 밀려나는지 확인합니다. 외곽선은 이 높이 계산 밖에 그려지므로 주변 간격도 따로 살핍니다.

확대와 리플로의 기준은 W3C 리플로 설명에서 확인할 수 있습니다. 아래 서재는 표준 폼의 이름과 제출 관계를 보완합니다. 완료 후에는 고정 높이를 최소 높이로 바꾼 이유, 표시된 포커스가 잘릴 수 있는 원인, 자동 검사와 실제 관찰이 각각 보장하는 범위를 설명합니다.

따라하기

starter와 실패 조건 확인

이 레슨 starter 압축을 새 폴더에 풀고 package.json이 있는 폴더에서 실행합니다. Node/npm과 Python 3가 필요하며 외부 의존 패키지는 없습니다.

npm ci
npm test

starter는 일부 CSS 계약이 FAIL인 상태입니다. check.py와 document-check.py는 고치지 않습니다. ENOENT는 작업 폴더, python3 실행 실패는 Python 설치부터 확인합니다.

해당 단계의 CSS 규칙 교체

assets/site.css 마지막 TODO 실패 블록을 다음 코드로 교체합니다. 이전 단계와 기본 글꼴·포커스 선언은 유지합니다. 동일 선택자의 실패 선언을 뒤에 남기지 않습니다.

input, select, button { padding: .625rem; min-height: 2.75rem; }
button { white-space: normal; max-width: 100%; }
a { color: #1645b5; text-decoration: underline; }
:focus-visible { outline: 3px solid #1645b5; outline-offset: 3px; }

index.html의 기존 폼 이름과 레이블, GET 목적지는 그대로 둡니다. 저장 후 브라우저를 새로고침합니다.

solution 기준 자동 검사

실습 폴더에서 실행합니다. 아래 출력은 모범 답안을 실제 실행한 결과입니다. 이 검사는 제공 CSS 선언 계약과 앞 모듈 HTML 계약을 확인하며 렌더링 검증 결과는 아닙니다.

npm test

실행 결과

> frontend-m02-focus-style@1.0.0 test
> python3 check.py

PASS 앞 모듈 HTML·폼 계약 18개 유지
PASS 긴 제목·URL·이미지 없는 카드 fixture
PASS index.html 뷰포트 및 공통 CSS
PASS event.html 뷰포트 및 공통 CSS
PASS results.html 뷰포트 및 공통 CSS
PASS 공통 border-box 및 카드 테두리
PASS 컨테이너 가변 너비와 여백
PASS 카드 padding을 포함하는 가변 너비
PASS 폼 Flex 줄바꿈과 필드 축소
PASS 목록 Grid와 gap
PASS 한 열 기본값과 두 열 분기
PASS 폼 이름 및 DOM 순서 보존
PASS 카드 최소 너비와 URL 줄바꿈
PASS 이미지 축소와 비율 유지
PASS 긴 설명 줄바꿈 유지
PASS 페이지 넘침 숨김과 고정 높이 없음
PASS 포커스 외곽선과 간격
PASS 조작 영역 여백과 최소 높이
PASS 버튼 여러 줄과 링크 밑줄
PASS 높이 고정과 포커스 제거 없음
CSS 계약 20개, 실패 0개
화면·200% 확대·실제 Tab 표시는 LAYOUT.md에 별도 기록합니다.

실제 화면과 사용자 흐름 기록

브라우저의 파일 열기로 index.html을 엽니다. 브라우저 메뉴로 200% 확대하고 검색어 책·지역 서울의 Enter/버튼 제출과 빈 입력 안내를 확인합니다. Tab·Shift+Tab으로 메뉴·검색어·지역·검색·상세 링크를 이동하며 포커스 잘림을 기록합니다.

콘솔에서 아래 숫자를 읽고 LAYOUT.md에 환경·페이지·폭·관찰을 남깁니다. 수치가 같아도 잘린 내용이나 포커스가 없는지 눈으로 확인합니다. 실행 전의 결과는 미확인으로 둡니다.

({scrollWidth: document.documentElement.scrollWidth,
  clientWidth: document.documentElement.clientWidth})

확인 문제

실습

200% 확대와 키보드 포커스에서 검색 버튼과 설명을 확인합니다. starter의 TODO CSS를 수정해 npm ci 후 npm test를 통과시키세요. 자동 검사는 제공 선언 계약만 확인합니다. HTML의 name·label·설명 참조·GET 제출·DOM 순서를 유지하고, 세 페이지 네 폭·200% 확대·키보드 실제 관찰을 LAYOUT.md에 기록합니다. 검사 파일은 수정하지 않습니다.

시작 코드·테스트 내려받기

실행 명령

npm test

기대 결과

CSS 계약 20개, 실패 0개. 화면·확대·키보드 결과는 LAYOUT.md에 별도 기록합니다.

모범 답안모범 답안 내려받기

더 읽기

면접 질문

  • 확대 시 버튼 문구가 잘리는 문제를 고정 높이와 최소 높이 관점에서 설명해 주세요.
  • :focus-visible 표시가 잘리거나 보이지 않을 때 어떻게 점검하나요?