컴포넌트와 상태
엘리베이터 버튼을 눌렀는데 불이 들어오지 않으면 사람들은 한 번 더, 또 한 번 더 누른다. 버튼이 "알아들었다"고 대답하지 않았기 때문이다. 웹 화면의 버튼도 같다. 마우스를 올렸을 때, 키보드로 다가왔을 때, 누르는 순간, 누를 수 없을 때, 누른 뒤 처리하는 동안, 화면은 매번 다르게 대답해야 한다. 그리고 그 버튼은 손가락 끝으로도 눌린다. 손가락은 마우스 포인터보다 훨씬 굵고, 걸으면서 누르면 더 흔들린다. 이 장은 웹 화면의 부품들이 상태로 말하는 법과, 손과 포인터가 실제로 닿을 수 있는 크기를 다룬다.
- 버튼·링크·입력 칸·카드를 하는 일로 구별하고, 가는 곳(링크)과 하는 일(버튼)에 맞는 HTML 요소를 고른다.
- 기본·호버·포커스·눌림·비활성·로딩 상태를 표로 설계하고, 상태마다 색 말고도 모양이나 글자로 다름을 알린다.
- 키보드 사용자에게 포커스 표시가 왜 필요한지 알고, 어떤 바탕에서도 보이는 포커스 링을 만든다(WCAG 2.2의 2.4.7, 1.4.11, 2.4.13).
- 고정 크기 대신 안쪽 여백과 최소 크기로 버튼을 짜서 사용자가 글자를 키워도 깨지지 않게 한다.
- 손가락 터치의 흩어짐으로 타깃 크기와 간격이 잘못 누르기에 미치는 영향을 보고, 24 CSS px(WCAG 2.5.8)와 44·48 같은 플랫폼 지침을 구별한다.
- 피츠의 법칙(섀넌 형식)으로 거리와 크기가 누르는 시간에 미치는 영향을 계산하고, 호버가 없는 기기를 위해 설계한다.
화면의 부품
웹 페이지는 수많은 작은 부품으로 이루어진다. 버튼, 링크, 입력 칸, 체크 상자, 카드, 탭, 알림 띠. 이렇게 한 번 설계해 두고 여러 화면에서 되풀이해 쓰는 화면의 부품을 컴포넌트(Component)라고 한다. 같은 버튼을 행사 페이지에서도, 신청 폼에서도, 신간 목록에서도 쓰므로, 버튼 하나를 잘 만들면 사이트 전체가 좋아지고 하나를 잘못 만들면 그 실수가 사이트 전체에 퍼진다. 6장에서 색과 글자를 토큰으로 정리한 것은 이 부품들을 만들 재료를 마련한 일이었다. 이 장은 그 재료로 부품을 조립한다.
부품을 고를 때 가장 먼저 물을 것은 생김새가 아니라 하는 일이다. 웹에서 가장 흔히 헷갈리는 두 부품이 링크와 버튼이다. 링크(Link)는 다른 곳으로 데려간다. 다른 페이지, 같은 페이지의 다른 자리, 내려받을 파일. 버튼(Button)은 지금 이 자리에서 무언가를 한다. 신청서를 보내고, 메뉴를 열고, 장바구니에 담는다. 생김새는 바꿀 수 있다. 버튼처럼 생긴 링크("행사 전체 보기")도, 링크처럼 생긴 버튼("더 불러오기")도 있다. 하지만 HTML에서는 하는 일에 맞는 요소를 써야 한다. 링크는 <a href>, 버튼은 <button>이다.
| 부품 | 하는 일 | HTML 요소 | 흔한 실수 |
|---|---|---|---|
| 링크 | 다른 곳으로 데려간다 | <a href="…"> | "여기를 클릭"처럼 어디로 가는지 모르는 문구(8장) |
| 버튼 | 이 자리에서 무언가를 한다 | <button> | <div>에 누르기만 붙여 키보드로 닿지 않는다 |
| 입력 칸 | 사람에게서 값을 받는다 | <input>, <textarea>, <label> | 라벨 없이 자리표시자만 둔다(9장) |
| 카드 | 한 대상(행사, 책)의 요약을 묶는다 | <article> 안의 제목 링크 | 카드 안의 모든 글자를 링크로 감싸 화면 낭독기가 긴 문장을 통째로 읽는다 |
<div>에 누르기 동작만 붙인 "가짜 버튼"은 마우스로는 잘 눌린다. 그래서 디자이너도 개발자도 문제를 모르고 지나간다. 그런데 키보드의 Tab 키로는 그 자리에 갈 수 없고, Enter나 스페이스로 누를 수도 없으며, 화면 낭독기는 그것이 버튼인 줄 모른다. 진짜 <button>은 이 모든 것을 브라우저가 공짜로 해 준다. 웹 표준은 이것을 부품의 이름, 역할, 값이 기계에 전달되어야 한다는 요구로 적어 둔다(WCAG 2.2 성공 기준 4.1.2, 이름·역할·값). 접근성 전체는 11장에서 다루고, 이 장에서는 부품이 사람에게 대답하는 방식, 곧 상태에 집중한다.
상태: 부품이 대답하는 방식
같은 버튼도 때에 따라 다른 얼굴을 한다. 부품이 지금 어떤 처지에 있는지를 상태(State)라고 한다. 버튼의 상태는 대개 여섯 가지로 정리한다. 아무 일도 없을 때의 기본, 마우스 포인터를 올렸을 때의 호버(Hover), 키보드로 다가와 지금 입력을 받을 차례가 되었을 때의 포커스(Focus), 누르고 있는 순간의 눌림(Active, Pressed), 지금은 누를 수 없을 때의 비활성(Disabled), 누른 뒤 처리를 기다리는 로딩(Loading).
| 상태 | 언제 | CSS에서 | 사람에게 알려야 할 것 |
|---|---|---|---|
| 기본 | 아무 일도 없을 때 | 기본 규칙 | "나는 누를 수 있다"(버튼다운 모양) |
| 호버 | 마우스 포인터가 위에 있을 때 | :hover | "지금 누르면 내가 눌린다" |
| 포커스 | Tab 키 등으로 차례가 왔을 때 | :focus-visible | "키보드 입력은 지금 여기로 온다" |
| 눌림 | 누르고 있는 동안 | :active | "알아들었다" |
| 비활성 | 조건이 맞지 않아 누를 수 없을 때 | :disabled, [aria-disabled="true"] | "지금은 안 된다, 그리고 그 이유" |
| 로딩 | 누른 뒤 서버의 대답을 기다릴 때 | 클래스, [aria-busy="true"] | "처리하는 중이다, 다시 누르지 않아도 된다" |
디자인 시안이 기본 상태 하나만 그려져 있으면, 나머지 다섯 상태는 개발자가 짐작으로 채우거나 브라우저의 기본값으로 남는다. 아래 시뮬레이터의 위쪽은 버튼 하나의 여섯 상태를 나란히 놓은 상태 표(State matrix)다. "첫 시안"은 호버 색만 정해 둔 버튼, "다섯 상태 + 로딩"은 상태마다 다른 단서를 준 버튼이다. 아래쪽에는 같은 두 설계로 만든 진짜 버튼이 있다. 마우스를 올려 보고, 페이지를 누른 다음 Tab 키로 다가가 보고, 눌러 본다.
버튼의 상태 표
첫 시안의 문제는 색이 아니라 침묵이다. 포커스가 왔는데 말하지 않고, 누를 수 없는데 말하지 않고, 처리 중인데 말하지 않는다. 사람은 대답이 없으면 다시 누른다. 그래서 로딩 상태가 없는 신청 버튼은 같은 신청을 두세 번 보내게 만든다. 서버에서 중복을 막는 일은 개발자의 몫이지만, "처리 중"이라고 말하는 일은 디자인의 몫이다.
개선안이 상태마다 쓴 단서를 다시 보자. 호버는 색만 조금 어둡게 했다. 호버는 포인터가 바로 그 자리에 있을 때만 생기므로 작은 변화로 충분하다. 포커스는 색이 아니라 링이다. 눌림은 더 어두운 색에 더해 버튼이 2 px 내려앉고 아래 그림자가 사라진다. 비활성은 회색으로 바뀌면서 글자도 "신청 마감"으로 바뀐다. 로딩은 돌아가는 표시와 "신청 중…"이다. 상태를 색 하나로만 알리면 색을 구별하기 어려운 사람(ColorBook 13장), 햇빛 아래의 휴대폰 화면, 흑백으로 보는 사람에게는 상태가 사라진다(WCAG 2.2 성공 기준 1.4.1, 색의 사용).
disabled 속성을 단 버튼은 키보드 포커스도 받지 않아서, 화면 낭독기 사용자는 버튼이 있다는 것조차 모를 수 있다. WCAG는 비활성 부품의 글자를 대비 요구에서 빼 주지만(성공 기준 1.4.3의 예외), 그렇다고 읽을 수 없게 흐리게 만들라는 뜻은 아니다. 대안은 둘이다. 버튼은 늘 눌리게 두고 누르면 무엇이 빠졌는지 알려 주거나(9장), 비활성으로 둔다면 바로 옆에 이유를 글자로 적는다("모집이 끝났습니다"). 이 장의 개선안이 비활성 버튼의 글자를 "신청 마감"으로 바꾼 것도 그 때문이다.
포커스: 키보드의 커서
모든 사람이 마우스나 손가락으로 웹을 쓰지는 않는다. 손을 정밀하게 움직이기 어려운 사람, 화면을 보지 않고 화면 낭독기로 쓰는 사람, 빠른 입력을 좋아하는 사람은 키보드로 페이지를 돌아다닌다. Tab 키를 누르면 다음 링크나 버튼으로, Shift+Tab을 누르면 이전으로 간다. 이때 지금 입력을 받을 부품이 어디인지 보여 주는 표시가 포커스 표시(Focus indicator)다. 글을 쓸 때 깜빡이는 커서와 같은 일을 한다. 커서가 보이지 않는 문서 편집기를 상상해 보면 포커스 표시가 없는 웹 페이지가 어떤지 알 수 있다.
브라우저는 포커스 받은 부품에 기본 링을 그려 준다. 그런데 이 링이 시안과 어울리지 않는다며 CSS 한 줄 outline: none으로 지워 버리는 일이 아주 흔하다. 마우스로 누를 때도 링이 생겨 보기 싫다는 이유다. 그 문제는 이제 :focus-visible이라는 선택자로 풀 수 있다. 브라우저가 "이 포커스는 키보드로 온 것이라 표시가 필요하다"고 판단할 때만 해당하는 상태라, 마우스로 누를 때는 링이 생기지 않고 키보드로 올 때만 생긴다.
.btn:focus-visible {
outline: 3px solid #22342a; /* 짙은 색 링 */
outline-offset: 2px; /* 버튼에서 2px 띄운다 */
}
포커스 표시에 관한 WCAG 2.2의 성공 기준은 여럿이다. 2.4.7(포커스 보임, AA)은 키보드로 조작할 수 있는 화면에서 포커스 표시가 보여야 한다고 요구한다. 1.4.11(텍스트가 아닌 콘텐츠의 대비, AA)은 부품의 상태를 알아보는 데 필요한 시각 정보가 이웃한 색과 3:1 이상 대비되어야 한다고 요구하고, 포커스 링도 여기에 든다. 2.2에서 새로 생긴 2.4.11(포커스 가려지지 않음 최소, AA)은 포커스 받은 부품이 고정된 머리 띠나 쿠키 알림에 완전히 가려지지 않아야 한다고 하고, 2.4.13(포커스 모양, AAA)은 링의 면적이 부품 둘레에 2 CSS px 두께로 두른 것 이상이고 포커스 전후의 같은 화소가 3:1 이상 달라져야 한다고 더 엄격하게 정한다.
포커스 링이 어려운 이유는 버튼이 늘 같은 바탕 위에 있지 않기 때문이다. 흰 본문 위의 버튼, 크림색 알림 띠 위의 버튼, 짙은 바닥글 위의 버튼에 같은 링을 쓰면 어딘가에서는 링이 바탕에 녹는다. 아래에서 버튼을 끌어 바탕을 바꿔 가며 링 스타일을 비교한다.
어떤 바탕에서도 보이는 포커스 링
바탕이 하나뿐인 사이트라면 짙은 링 하나로 충분하다. 바탕이 여러 가지라면 두 가지 방법이 있다. 바탕마다 링 색을 바꾸는 규칙을 정하거나(어두운 영역 안에서는 밝은 링), 서로 대비가 큰 두 색을 겹친 이중 링을 쓴다. 이중 링은 규칙이 하나라 개발자가 실수할 여지가 적고, 바탕 위에 사진이 깔린 곳처럼 바탕색을 미리 알 수 없는 자리에서도 버틴다. 마지막 단계에서 본 것처럼, 링을 아무리 잘 만들어도 버튼 자체가 바탕에 녹으면 소용없다. 책방 초록 띠 위에는 크림색 버튼을 놓는 식으로 부품의 변형을 따로 둔다.
:focus-visible을 "브라우저가 포커스를 눈에 띄게 표시해야 한다고 판단할 때"로만 정하고, 판단 방법은 브라우저에 맡긴다. 주요 브라우저는 대체로 키보드로 포커스를 옮겼을 때 켜고, 마우스로 버튼을 눌렀을 때는 켜지 않는다. 다만 글자를 입력하는 칸은 마우스로 눌러도 켜지는 경우가 많다. 곧 입력할 자리이기 때문이다. 판단 규칙이 브라우저마다 조금씩 다를 수 있으므로 결과는 실제 브라우저에서 키보드로 확인한다. 포커스 표시를 지우는 대신 :focus-visible로 바꾸는 것만으로도 "마우스로 누르면 링이 생겨 싫다"는 불만과 접근성을 함께 해결할 수 있다.
크기는 내용이 정한다
그래픽 디자인 도구에서 버튼을 그리면 사각형부터 그리게 된다. 가로 120, 세로 36. 그 안에 글자를 넣는다. 화면이 이 그림대로만 보인다면 문제가 없다. 하지만 2장에서 본 것처럼 사람들은 휴대폰과 브라우저의 글자 크기 설정을 바꾼다. 한도경처럼 글자를 가장 크게 해 두고 쓰는 사람에게 120×36 상자 안의 "모임 신청하기"는 상자 밖으로 넘치거나 잘린다. 번역된 페이지에서 글자 수가 늘어날 때도 같은 일이 생긴다.
그래서 웹의 부품은 크기를 고정하지 않고 내용 + 안쪽 여백 + 최소값으로 정한다. 버튼의 폭은 글자 폭에 좌우 여백을 더한 만큼, 높이는 글줄 높이에 위아래 여백을 더한 만큼이되 손가락으로 누르기에 모자라지 않게 최소 높이를 둔다. 글자 크기는 6장에서 정한 대로 rem으로 적어 사용자 설정을 따른다. 아래에서 휴대폰 화면(폭 360)의 행사 카드에 들어간 두 버튼을 두 방식으로 짜고 글자 크기를 키워 본다.
글자가 커지면 버튼은?
안쪽 여백으로 짠 버튼은 글자가 두 배가 되어도 읽힌다. 대신 화면을 더 차지하고, 줄이 바뀌고, 옆의 부품을 밀어낸다. 이것은 실패가 아니라 웹이 원래 그렇게 움직이는 방식이다. 디자이너의 일은 "글자가 커졌을 때 무엇이 어디로 가는가"까지 정해 두는 것이다. 두 버튼이 한 줄에 들어가지 않으면 주 버튼을 위로 쌓는다, 버튼 글자는 두 줄까지 허용한다, 같은 규칙들이다. 3장에서 정한 간격 단계와 "안쪽 간격 < 바깥 간격" 규칙이 여기서 그대로 쓰인다.
손가락이 닿는 크기
마우스 포인터의 끝은 화소 하나다. 손가락은 그렇지 않다. 화면에 닿는 손가락 끝의 면적은 넓고, 사람은 자기가 누른 자리를 정확히 알지 못하며, 손가락 끝이 누르려는 곳을 가린다. 버스 안이나 걸으면서 한 손 엄지로 누르면 더 흔들린다. 그래서 같은 자리를 노리고 여러 번 눌러도 닿는 자리는 노린 점 둘레에 흩어진다. 손가락이나 포인터로 누를 수 있는 부품의 면을 터치 타깃(Touch target, 누르는 면)이라고 한다. 눈에 보이는 모양보다 누르는 면을 더 크게 잡을 수도 있다.
누르는 면의 크기에 관해서는 성격이 다른 기준 몇 가지가 함께 돌아다닌다. 하나로 섞어 쓰면 안 된다.
| 기준 | 값 | 성격 |
|---|---|---|
| WCAG 2.2 성공 기준 2.5.8 타깃 크기(최소), AA | 24×24 CSS px | 접근성 표준의 하한. 더 작으면 둘레에 충분한 간격이 있어야 한다(아래) |
| WCAG 2.2 성공 기준 2.5.5 타깃 크기(강화), AAA | 44×44 CSS px | 더 엄격한 등급의 권고 |
| Apple Human Interface Guidelines | 44×44 pt | iOS 앱 설계 지침의 최소 누르는 면 |
| Material Design(구글) | 48×48 dp | 안드로이드 앱 설계 지침. 지침은 이것이 실제 크기로 약 9 mm라고 설명한다 |
2.5.8의 24는 "이보다 작으면 많은 사람이 누르기 어렵다"는 최소선이지 목표가 아니다. 이 기준에는 예외가 있다. 누르는 면이 24보다 작아도, 그 면의 가운데에 지름 24 CSS px의 원을 그렸을 때 그 원이 다른 타깃이나 다른 작은 타깃의 원과 겹치지 않으면 통과한다. 크기가 모자라면 간격으로 메우라는 뜻이다. 문장 안의 링크처럼 글줄에 섞인 타깃도 예외다. 44와 48은 앱 플랫폼의 설계 지침에서 왔고, 단위(pt, dp)가 CSS px와 같은 개념은 아니지만 휴대폰에서는 대체로 비슷한 크기가 된다.
아래 시뮬레이터는 휴대폰의 행사 날짜 줄이다. 칩 하나를 노리고 손가락으로 여러 번 누른 자리를 점으로 찍는다. 노린 칩에 닿으면 초록, 옆 칩에 닿으면 빨강, 아무것도 없는 틈이나 줄 밖에 닿으면 회색이다. 칩을 눌러 노릴 칩을 바꿀 수 있다.
손가락은 흩어져 닿는다
잘못 누르기를 정하는 것은 칩 크기 하나가 아니라 이웃한 칩까지의 거리다. 칩의 가운데에서 옆 칩의 가장자리까지가 멀수록 흩어진 손가락이 옆 칩에 닿기 어렵다. 그 거리는 칩 크기의 절반에 간격을 더한 것이다. 그래서 칩을 키우는 것과 간격을 넓히는 것은 둘 다 잘못 누르기를 줄인다. 다른 점은, 칩을 키우면 노린 칩에 닿는 비율도 오르지만 간격을 넓히면 그 몫이 "반응 없음"으로 간다는 것이다. 화면 폭에 여유가 있으면 칩을 키우고, 폭이 모자라면 적어도 간격을 둔다.
피츠의 법칙: 멀고 작으면 느리다
심리학자 폴 피츠(Paul M. Fitts)는 1954년 논문(「The information capacity of the human motor system in controlling the amplitude of movement」, Journal of Experimental Psychology)에서 사람이 손을 뻗어 목표를 짚는 데 걸리는 시간을 다뤘다. 목표가 멀수록, 목표가 작을수록 시간이 더 걸린다는 것, 그리고 그 관계가 거리와 크기의 비의 로그로 잘 정리된다는 것이다. 이것을 피츠의 법칙(Fitts's law)이라고 한다. 오늘날 화면 설계에서 가장 많이 쓰는 형태는 스콧 매켄지(I. Scott MacKenzie)가 1992년 논문(「Fitts' law as a research and design tool in human-computer interaction」, Human–Computer Interaction)에서 정리한 섀넌 형식이다.
식에서 가장 중요한 것은 시간이 거리나 크기 하나가 아니라 둘의 비에 달려 있다는 점이다. 멀리 있는 목표도 크면 쉽고, 가까운 목표도 아주 작으면 어렵다. 그리고 로그 때문에 이미 큰 목표를 더 키우는 효과는 점점 줄어든다. 아래에서 목표를 끌어 거리를 바꾸고, 폭을 바꿔 그래프 위의 점이 어떻게 움직이는지 본다.
거리와 폭의 비
피츠의 법칙이 화면 설계에 주는 처방은 단순하다. 자주 누르는 것은 크게, 가깝게. "신청하기"처럼 그 페이지의 목적인 버튼은 크게 만들고, 사람의 손이나 포인터가 이미 있는 자리 가까이 둔다. 반대로 "신청 취소"처럼 실수로 누르면 곤란한 것은 일부러 작게, 멀리 두기도 한다. 피츠의 법칙을 거꾸로 쓰는 것이다. 마우스를 쓰는 넓은 화면에서는 화면 가장자리도 특별하다. 포인터가 화면 끝에서 멈추기 때문에 가장자리에 붙은 목표는 움직이는 방향으로 폭이 끝없이 넓은 것과 같다. 운영체제들이 메뉴 막대나 작업 표시줄을 화면 끝에 두는 이유로 자주 설명된다. 손가락에는 이 효과가 없다. 화면 끝을 넘어 손가락이 멈추지는 않기 때문이다.
호버가 없는 기기
호버는 마우스의 상태다. 손가락에는 "올려 두기"가 없다. 화면에 닿는 순간이 곧 누르는 순간이다. 그런데 넓은 화면에서 설계를 시작하면 정보를 호버 뒤에 숨기기 쉽다. 행사 카드에 포인터를 올리면 "남은 자리"와 "신청하기"가 나타나는 식이다. 화면이 깔끔해 보이기 때문이다. 이 정보는 휴대폰에서 어떻게 될까. 브라우저마다 다르다. 어떤 브라우저는 첫 탭을 호버로 처리해 숨은 정보를 보여 주고 두 번째 탭에서야 누른 것으로 친다. 어떤 브라우저는 첫 탭에 곧바로 카드의 링크로 넘어가 숨은 정보를 볼 기회가 없다. 키보드 사용자에게는 :hover만으로 숨긴 정보가 아예 나타나지 않는다.
호버 뒤에 숨긴 정보
원칙은 하나다. 호버는 꾸밈으로만 쓰고, 정보와 동작은 호버 없이도 닿게 한다. 호버로 그림자를 짙게 하거나 색을 조금 바꾸는 것은 좋다. 그러나 남은 자리, 가격, 신청 버튼처럼 결정에 필요한 것은 늘 보이게 둔다. CSS에는 기기의 포인터 성격을 묻는 미디어 쿼리가 있다(Media Queries Level 4). (hover: hover)는 주된 입력 장치가 호버할 수 있는지, (pointer: coarse)는 주된 포인터가 손가락처럼 정밀하지 않은지를 묻는다. 호버 꾸밈을 @media (hover: hover) 안에 넣으면 휴대폰에서 탭한 뒤 호버 색이 남아 있는 어색한 모습을 막을 수 있다. 다만 노트북에 터치스크린이 달린 경우처럼 한 기기에 여러 입력이 섞여 있을 수 있으므로, 이 질문은 꾸밈을 고를 때만 쓰고 정보를 숨길지 말지를 정하는 데는 쓰지 않는다.
@media (hover: hover) {
.card:hover { box-shadow: 0 4px 16px rgb(0 0 0 / .12); }
}
/* 남은 자리와 신청 버튼은 미디어 쿼리 밖, 늘 보이게 */
호버나 포커스로 나타나는 내용(말풍선 같은 것)을 꼭 써야 할 때도 규칙이 있다. WCAG 2.2 성공 기준 1.4.13(호버나 포커스로 나타나는 콘텐츠, AA)은 그 내용을 Esc 키 등으로 닫을 수 있어야 하고, 포인터를 그 내용 위로 옮겨도 사라지지 않아야 하며, 사람이 치우기 전까지 머물러야 한다고 요구한다. 화면 확대를 쓰는 사람이 말풍선을 읽으려고 포인터를 옮기다가 말풍선이 사라지는 일을 막기 위해서다.
서윤의 웹 개편 노트
"신청하기"가 대답하게 하기
6장에서 토큰을 정리한 다음 날, 지후가 개발 중인 행사 페이지를 키보드로만 써 보다가 손을 멈췄다. "Tab을 누를 때마다 어딘가로 가긴 하는데, 어디 있는지 모르겠어요." 서윤의 시안에서 "신청하기" 버튼에 정해 둔 상태는 호버 색 하나뿐이었다. 개발 템플릿에는 포커스 링을 지우는 outline: none이 들어 있었다. 모집이 끝난 행사의 버튼은 겉모습은 그대로인데 눌러도 아무 일이 없었고, 신청을 보내는 동안에도 버튼이 가만히 있어 시험 삼아 누른 주인의 신청이 두 번 들어갔다.
같은 주에 도경이 휴대폰으로 행사 날짜를 고르다가 옆 날짜를 눌렀다. 날짜 칩은 28 px에 간격 4 px. 도경은 열 번 중 서너 번은 옆 날짜를 눌렀다고 했다(가상 기록, 도경 한 사람의 간이 시험). 글자를 가장 크게 해 둔 도경의 휴대폰에서는 120×36으로 고정한 "모임 신청하기" 버튼의 글자 끝이 잘려 "모임 신청하"까지만 보였다.
서윤은 버튼 하나를 위해 상태 표를 처음부터 다시 그렸다. 호버는 책방 초록을 OKLCH 명도만 낮춘 색, 눌림은 더 낮춘 색에 2 px 내려앉기를 더했다. 포커스는 색이 아니라 링으로 정했다. 짙은 바닥글 위에서 짙은 링이 사라지는 것을 이 장의 시뮬레이터로 확인하고, 어두운 영역 안에서는 크림색 링을 쓰는 규칙을 더했다. 지후는 이중 링을 제안했지만, 서윤은 바탕이 흰 본문과 짙은 바닥글 두 가지뿐이라 규칙 하나로 충분하다고 보았다. 지후는 "사진 위에 버튼을 올리는 날이 오면 이중 링으로 가요"라고 메모했다.
터치 타깃은 기준을 고르는 데 시간이 걸렸다. WCAG 2.5.8의 24 px는 넘기면 되는 최소선이고, 서윤은 도경의 손과 걸으면서 누르는 손님을 생각해 앱 지침과 같은 44 px를 사이트의 최소로 정했다. 날짜 칩을 44로 키우면 휴대폰 한 줄에 일주일이 다 들어가지 않는다. 서윤은 칩을 줄이는 대신 날짜 줄을 가로로 넘기게 하고, 끝의 칩이 반쯤 잘려 보이게 해서 더 있다는 단서를 주었다. 호버에만 보이던 "남은 자리"는 카드에 늘 보이게 옮겼다.
| 항목 | 서윤의 선택 | 확인한 값 |
|---|---|---|
| 기본 | 책방 초록 #2f7d5b, 흰 글자, 아래 그림자 2 px | 흰 글자 대비 4.99 |
| 호버 | #186b4a(OKLCH 명도 0.53 → 0.47), @media (hover: hover) 안에서만 | 흰 글자 대비 6.48 |
| 포커스 | :focus-visible, 3 px 링 #22342a, 2 px 띄움. 어두운 영역 안에서는 #ffe2d7 링 | 링 대비: 흰 바탕 13.19, 바닥글 위 크림 링 10.74 |
| 눌림 | #025f3f(명도 0.43), 2 px 내려앉고 그림자 사라짐 | 흰 글자 대비 7.75 |
| 비활성 | #d9dfdb 바탕, #5f6b65 글자, 글자를 "신청 마감"으로 | 글자 대비 4.11(대비 예외지만 읽히게) |
| 처리 중 표시 | 눌림 다음에 돌아가는 표시와 "신청 중…", 그동안 다시 누르기 무시(aria-busy) | 중복 신청 막기는 서버에서도 |
| 크기 | 고정 크기 없음. 안쪽 여백 위아래 10·좌우 20 px, 최소 높이 44 px, 글자는 rem | 글자 200%에서도 잘리지 않음 |
| 터치 타깃 | 사이트 최소 44×44 CSS px(WCAG 2.5.8의 24는 하한) | 날짜 칩·메뉴·닫기 단추 모두 |
| 날짜 칩 | 44×44, 칩 사이 8 px(3장의 간격 단계), 줄은 가로로 넘김 | 모형: 걸으면서도 잘못 누르기 2% 안팎(28·4일 때 약 11%) |
핵심 정리
- 컴포넌트는 되풀이해 쓰는 화면의 부품이다. 생김새보다 하는 일로 고른다. 데려가면 링크(
<a href>), 이 자리에서 하면 버튼(<button>). - 부품은 기본·호버·포커스·눌림·비활성·로딩 상태로 대답한다. 상태 표로 빈칸 없이 설계하고, 상태마다 색 말고도 링·모양·글자 같은 단서를 하나 이상 준다.
- 포커스 표시는 키보드 사용자의 커서다. 지우지 말고
:focus-visible로 바꾼다. 링은 어떤 바탕에서도 3:1 이상 보여야 한다(WCAG 2.4.7, 1.4.11, 2.4.13). - 부품의 크기는 고정하지 않고 내용 + 안쪽 여백 + 최소값으로 정한다. 사용자가 글자를 키워도 글자가 잘리지 않는다.
- 손가락 터치는 노린 점 둘레에 흩어진다. 잘못 누르기는 이웃까지의 거리(크기의 절반 + 간격)가 정한다. WCAG 2.5.8의 24 CSS px는 하한이고, 44·48은 플랫폼 지침이다.
- 피츠의 법칙: \(MT = a + b \log_2(D/W + 1)\). 시간은 거리와 폭의 비에 달려 있다. 자주 누르는 것은 크고 가깝게. 호버는 꾸밈으로만 쓰고 정보와 동작은 호버 없이 닿게 한다.
확인 퀴즈
1. WCAG 2.2의 성공 기준 2.5.8(타깃 크기 최소, AA)이 요구하는 누르는 면의 최소 크기와 그 예외를 바르게 설명한 것은?
2. 피츠의 법칙(섀넌 형식)으로 볼 때, 버튼을 지금보다 두 배 먼 곳으로 옮기면서 폭도 두 배로 키우면 어려움의 지수 ID는 어떻게 되는가?
3. 개발 템플릿에 "마우스로 누를 때 생기는 테두리가 보기 싫다"는 이유로 button:focus { outline: none; }가 들어 있다. 가장 알맞은 고침은?
:focus-visible을 쓰면 마우스로 누를 때는 링이 생기지 않아 원래의 불만도 해결된다. 호버는 포인터의 상태라 포커스와 다르고, 아주 작은 색 차이는 1.4.11의 3:1을 넘기 어렵고 색을 구별하기 어려운 사람에게는 사라진다.4. 넓은 화면 시안에서 행사 카드의 "남은 자리"와 "신청하기"가 포인터를 올릴 때만 나타난다. 휴대폰과 키보드 사용자를 생각한 판단으로 가장 알맞은 것은?
:hover로 숨긴 정보에 닿지 못한다. 길게 누르기는 발견하기 어렵다. 미디어 쿼리는 한 기기에 여러 입력이 섞일 수 있어 정보를 숨길지 정하는 데 쓰기에 믿을 만하지 않다. 결정에 필요한 정보는 늘 보이게 둔다.