대부분의 UI를 바로잡는 다섯 가지 여백 결정
인터페이스가 어딘가 어색한데 그 이유를 말로 설명할 수 없다면, 답은 대개 여백에 있습니다. 팔레트도 서체도 아닌 그 사이의 간격입니다. 여백은 무엇이 한 덩어리이고 무엇이 별개이며 무엇이 가장 중요한지를 눈에게 알려 주는 보이지 않는 시스템이며, 무너질 때도 소리 없이 무너집니다. 13px 간격을 경고해 주는 linter는 기본으로 존재하지 않고, 리뷰에 “리듬이 깨졌다”라고 쓰는 사람도 없습니다. 제 인터페이스와 다른 사람의 인터페이스를 여러 해 감사해 왔지만, 제가 손대는 여백 수정은 거의 전부 같은 다섯 가지 결정 중 하나입니다. {.answer-block}
TL;DR
- 스케일을 정하고, 거기서 벗어난 값은 모두 거부합니다. 4, 8, 16, 24, 32, 48, 64. 13px 간격은 디자인 결정이 아니라 드리프트입니다.
- 그룹 사이의 여백은 그룹 안의 여백보다 커야 합니다. 이 게슈탈트 원리 하나가 대부분의 테두리와 박스를 대신합니다.
- 모든 컨테이너에 역할에서 도출한 padding 하나만 부여합니다(컴포넌트 16, 카드 24, 섹션 32-48). 면마다 눈대중으로 맞추지 마십시오.
- 구분선에 손을 뻗기 전에 여백이 위계를 만들게 하십시오. 테두리는 여백이 포기했을 때 남는 모습입니다.
- 수직 리듬은 텍스트 층위에서 바로잡습니다. line-height는 최소 1.5, 제목 뒤 여백은 앞 여백보다 작게. 이 층위가 무너지면 그 위의 어떤 조정으로도 페이지를 구할 수 없습니다.
1. 스케일을 정하고 벗어난 값은 모두 거부합니다
어떤 값을 고르느냐는 첫 번째 결정에서 작은 쪽 절반에 불과합니다. 큰 쪽 절반은 값의 집합이 존재한다는 데 합의하는 일입니다. 제 기준은 4px이고 4, 8, 16, 24, 32, 48, 64의 일곱 개 값에 토큰을 하나씩 붙입니다. (데이터 테이블이나 툴바처럼 밀도가 높은 면에서는 12가 정당화되기도 합니다. 필요하다면 토큰으로 만드십시오. 죄는 이름 없는 값이지 여덟 번째 토큰이 아닙니다.)
| 토큰 | 값 | 용도 |
|---|---|---|
| xs | 4px | 인라인 요소, 아이콘 간격 |
| sm | 8px | 한 그룹 안의 관련 항목 |
| md | 16px | 표준 컴포넌트 padding |
| lg | 24px | 카드 padding, 섹션 내부 간격 |
| xl | 32px | 주요 섹션 분리 |
| 2xl | 48px | 섹션 최대값, 페이지 단위 최소값 |
| 3xl | 64px | 히어로 섹션 |
구체적인 숫자보다 중요한 것은 거부하는 태도입니다. 임의의 값(13px, 7px, 22px)은 리듬을 깨뜨리고, 눈은 그것을 “엉성함”으로 감지하면서도 정체를 이름 붙이지 못합니다. 제가 스스로에게 지우는 규칙은 이렇습니다. 모든 여백 값은 스케일에서 가져오거나 문서화된 이유를 갖는다. 실제로 문서화된 예외가 리뷰를 통과해 살아남는 일은 거의 없습니다. “그때는 그게 맞아 보였다”라는 감각이야말로 스케일이 막으려는 드리프트 그 자체이기 때문입니다.
스케일은 결정의 수도 줄여 줍니다. 스케일이 없으면 모든 간격이 구현 속도에 맞춰 내려지는 새로운 판단이 됩니다. 스케일이 있으면 대부분의 간격에는 그럴듯한 토큰이 정확히 하나만 존재하고, 디자인 리뷰는 산수 대신 정말 흥미로운 사례를 논의하게 됩니다.
2. 그룹 사이 여백 > 그룹 안 여백
이 글에서 한 문장만 가져간다면 이것입니다. 그룹 사이의 간격은 그룹 안의 간격보다 눈에 띄게 커야 합니다. 게슈탈트의 근접성이 구조를 떠받치는 일을 하는 상태입니다. 눈은 상대적인 거리를 관계로 읽습니다. 자동으로, 그리고 말보다 먼저.
라벨과 입력 필드의 거리가 8px이고 필드끼리는 24px 떨어진 폼에는 박스도, 구분선도, 배경 색조도 필요 없습니다. 그룹이 저절로 드러나기 때문입니다. 이 거리를 일률적인 16px로 평탄하게 만들면 같은 폼이 등간격 행의 안개로 바뀌고, 사용자는 의식적으로 해석해야 합니다. 같은 세 개의 필드를 두 가지 방식으로 놓아 봅니다.
grouped (8 within / 24 between) flattened (16 everywhere)
Name Name
[______________]
[______________]
Email Email
[______________]
[______________]
Phone Phone
[______________]
[______________]
왼쪽에서는 각 라벨이 자기 필드의 라벨이라는 점에 의심의 여지가 없습니다. 오른쪽에서는 안쪽 라벨이 두 필드 사이에 정확히 같은 거리로 떠 있습니다. 근접성이 투표를 멈춘 것이고, 독자는 눈이 아니라 읽는 순서로 소속을 따져야 합니다.
실패는 거의 언제나 잘못된 층위에서의 압축입니다. 첫 화면에 더 담으려고 디자이너가 그룹 사이 간격을 좁히면 비율이 무너지고, 그룹이 풀리는 느낌이 들자 그것을 되살리려고 테두리를 더합니다. 그래서 네 번째 결정으로 이어지지만, 그 전에 하나가 더 있습니다.
3. 컨테이너마다 padding 하나, 역할에서 도출합니다
컨테이너는 부엌에 잡동사니 서랍이 늘어나듯 비대칭 padding을 쌓아 갑니다. 버튼을 넣으려고 여기를 조금, 아이콘 때문에 저기를 조금. 그러다 보면 카드의 위쪽은 18px, 왼쪽은 14px이 되어 있고 그 이유를 기억하는 사람은 없습니다. 해법은 언제나 같은 방식으로 도출하는 것입니다. 컨테이너의 padding은 내용물이 아니라 역할에서 나옵니다.
- 컴포넌트(버튼, 입력창, 칩): 16px
- 카드와 묶인 콘텐츠: 24px
- 섹션: 32-48px
- 페이지 단위와 히어로 영역: 48-64px
콘텐츠가 역할의 padding에 들어가지 않는다면 그 컨테이너에 대해 콘텐츠가 잘못된 것입니다. 나누거나, 줄이거나, 컨테이너를 한 단계 승격시키십시오. 어색한 자식 하나를 구하려고 padding을 조정하면 형제 요소 전체의 리듬이 깨지고, 그 비대칭은 끝내 언급하지 않을 사용자에게도 버그로 읽힙니다.
4. 테두리보다 여백이 먼저입니다
구분선의 대부분은 변명입니다. 리스트 행마다 그어진 밑줄, 폼 섹션마다 두른 박스, 카드와 카드 사이의 헤어라인. 하나하나가 포기한 여백의 모습입니다. 경계선이 그룹을 만드는 것은 사실입니다(게슈탈트의 공통 영역 원리는 실재합니다). 다만 그것은 센 약입니다. 시각적으로 시끄럽고, 더해지고, 누적됩니다. 테두리가 하나 늘 때마다 눈이 처리해야 할 선이 하나 늘어납니다.
제 작업 순서는 이렇습니다. 먼저 근접성 비율을 시도합니다(두 번째 결정). 그래도 그룹이 분리되지 않으면 선을 긋기 전에 배경 변화를 시도합니다. 옅은 색면은 선을 더하지 않고도 영역을 만들며, 공통 영역에 의한 그룹화 중 가장 조용한 형태이고, 응집은 필요하지만 가장자리는 필요 없는 그룹에서는 언제나 테두리보다 낫습니다. 테이블의 얼룩말 줄무늬가 이 원리의 가장 익숙한 모습입니다. 테두리는 영역에 정말로 단단한 가장자리가 필요할 때, 즉 테이블, 편집 가능한 영역, 중첩된 카드 같은 경우에만 꺼내고, 쓰기로 했다면 끝까지 일관되게 쓰십시오. 여기서는 여백으로 묶고 저기서는 테두리로 묶는 인터페이스는 두 개의 디자인 시스템이 싸우는 것처럼 읽힙니다.
리뷰에서 제가 적용하는 시험은 이렇습니다. 허용된 단단한 가장자리, 즉 테이블과 편집 가능한 필드를 표시하지 않는 테두리를 전부 지우고 무엇이 남는지 봅니다. 살아남은 그룹화는 진짜였습니다. 풀려 버린 것은 테두리가 대신 갚아 주던 여백의 부채였습니다.
5. 수직 리듬은 텍스트 층위에서 바로잡습니다
여백 시스템은 아래에서부터 죽습니다. 본문의 line-height가 1.2라면 섹션 여백을 아무리 넉넉히 줘도 페이지는 살아나지 않습니다. 문단 자체가 답답하게 읽히고, 그 위에 쌓은 모든 것이 그 긴장을 물려받습니다. 하한선은 이렇습니다. 본문 16px, line-height 1.5(긴 글은 1.75까지), 한 줄 길이는 65ch 안팎. 이 중 경험적 근거가 있는 부분은 line-height입니다. 접근성 지침이 1.5를 명시한 데에는 이유가 있고, 답답한 행간은 웹에서 가장 흔한 리듬 파괴 요인입니다. 65ch라는 상한은 법칙이라기보다 실무 감각입니다. 긴 줄도 따로 떼어 놓고 시험하면 문제없어 보일 수 있지만, 다음 줄 첫머리로 시선을 되돌리는 동작의 착지가 어려워지고, 독자는 논지를 만나기 훨씬 전에 글자의 벽부터 만납니다.
문단보다 위 층위에서 나머지를 조직하는 규칙은 이것입니다. 제목은 자신이 뒤따르는 내용보다, 자신이 이끄는 내용에 더 가까이 놓입니다. 앞 여백을 뒤 여백보다 크게, 대개 두 배가 기준입니다. 섹션 사이에 같은 거리로 떠 있는 제목은 어느 쪽에도 속하지 않습니다. 그 비대칭이야말로 제목을 자기 내용에 묶어 주는 힘입니다. 같은 제목을 두 가지 방식으로 놓아 봅니다.
bound (before wide / after tight) floating (equal both sides)
...end of previous section. ...end of previous section.
Shipping address Shipping address
[Street___________]
[City_____________]
[Street___________]
[City_____________]
왼쪽에서는 제목이 자기 필드를 소유하고 있음이 눈에 보입니다. 오른쪽에서는 제목이 섹션 사이를 떠돌고, 독자는 시각이 아니라 관습으로 소속을 배정합니다. 이는 두 번째 결정이 이번에는 텍스트 칼럼 안쪽에서 작동하는 것이며, 바로 그 점이 핵심입니다. 수직 리듬은 별개의 분야가 아니라 근접성 규칙을 가장 작은 스케일에 적용한 것입니다.
점검표
다섯 가지 결정은 어떤 화면에나 적용할 수 있는 리뷰 체크리스트로 압축됩니다.
- 모든 간격이 스케일 위에 있습니까?(벗어난 값은 들여다보십시오. 언제나 사고입니다.)
- 그룹 사이 간격이 그 그룹 안의 간격보다 큽니까?
- 각 컨테이너가 역할에서 도출한 padding을 하나만 갖고 있습니까?
- 뒤에 있는 여백을 고치면 지울 수 있는 테두리가 있습니까?
- 텍스트 칼럼이 자기 리듬을 지키고 있습니까? line-height 1.5 이상, 제목은 자기 내용에 묶여 있습니까?
대부분의 화면은 이 중 두세 개에서 떨어지고, 그것을 고쳤을 때의 인상 변화는 어떤 팔레트나 서체 교체보다 큽니다. 제 iOS 앱에서는 첫 번째 질문을 기계적으로 강제합니다. 커밋이 나가기 전에 리뷰 게이트가 diff에서 날것의 padding 숫자를 grep하고, 키트 토큰이 아닌 값은 살아남기 위해 이름 붙은 이유가 필요합니다. 여백은 장인정신이 숨는 자리입니다. 아무도 그것을 손가락으로 가리킬 수 없기 때문에 더욱 그렇습니다.
자주 묻는 질문
디자인 시스템은 어떤 여백 스케일을 써야 합니까?
4px 기준 스케일, 즉 4, 8, 16, 24, 32, 48, 64면 인터페이스에 필요한 거의 모든 간격을 감당합니다. 정확한 값보다 중요한 것은 배타성입니다. 모든 간격은 스케일에서 나오거나 문서화된 근거를 갖습니다. 임의의 값(7px, 13px, 22px)은 사용자가 느끼면서도 이름 붙이지 못하는 방식으로 시각적 리듬을 깨뜨립니다.
테두리 없이 UI 요소를 묶으려면 어떻게 합니까?
근접성 비율을 씁니다. 그룹 사이 여백이 그룹 안 여백보다 눈에 띄게 커야 합니다. 라벨과 필드 사이가 8px이고 다음 라벨까지가 24px이면 박스를 하나도 쓰지 않고 폼이 묶입니다. 테두리는 단단한 가장자리가 필요한 영역, 즉 테이블과 편집 가능한 영역에 아껴 두고, 그 전 단계로는 배경 변화를 우선하십시오.
카드와 버튼의 padding은 얼마나 주어야 합니까?
컨테이너의 역할에서 도출하십시오. 컴포넌트(버튼, 입력창)는 약 16px, 카드는 24px, 섹션은 32-48px, 히어로 영역은 그 이상입니다. 컨테이너마다 값은 하나입니다. 콘텐츠가 역할의 padding에 들어가지 않는다면 면을 하나하나 손보지 말고 콘텐츠를 바꾸거나 컨테이너를 승격시키십시오.
타이포그래피에서 수직 리듬이란 무엇입니까?
텍스트 칼럼을 따라 내려가며 일관되게 유지되는 여백 패턴입니다. 본문은 최소 16px에 line-height 1.5-1.75, 한 줄에 65자 안팎, 그리고 제목은 뒤따르는 내용보다 이끄는 내용에 더 가까이 놓습니다. 그보다 큰 모든 것이 이 층위에서 상속받습니다. 문단이 답답하다면 섹션 여백으로는 페이지를 고칠 수 없습니다.