17 KiB
layout — 레이아웃과 타이포그래피
4-1 단계에서 읽는다. 이 단계가 끝나면 아무 이펙트 없이도 완성된 페이지여야 한다. 그것이 이후 모든 폴백의 기반이다.
1. 그리드를 먼저 정한다
하나의 그리드에서 모든 것이 나온다
.page {
display: grid;
grid-template-columns:
[full-start] minmax(var(--space-4), 1fr)
[wide-start] minmax(0, 12rem)
[main-start] minmax(0, var(--measure)) [main-end] /* 3단계에서 정한 값이다 */
minmax(0, 12rem) [wide-end]
minmax(var(--space-4), 1fr) [full-end];
}
.page > * { grid-column: main; }
.page > .wide { grid-column: wide; }
.page > .full { grid-column: full; }
main 폭에 고정값을 쓰지 마라. --measure 는 3단계에서 언어와 폰트에 맞춰 정한 값이다.
여기에 60rem 같은 숫자를 넣으면 본문이 measure 를 넘어가 이 문서의 통과 조건을 스스로 어긴다.
ch는 선택한 글꼴의 메트릭에 의존한다. 한글과 라틴의 실제 줄 길이는 글꼴·언어·내용으로 달라지므로, 예시 폭을 보편 수치로 쓰지 말고 대표 문단을 렌더해 확인한다.
이 한 번의 정의로 본문 폭 · 넓은 블록 · 전체 폭이 전부 정렬된다. 섹션마다 max-width 와 padding 을 다시 쓰면 반드시 어긋난다.
비대칭을 두려워하지 마라
12열 중앙 정렬은 안전하고 잊힌다. 실제로 기억되는 레이아웃은 의도적으로 한쪽으로 몰려 있다. 다만 비대칭에도 규칙이 있어야 한다 — 아무 데나 놓는 것은 비대칭이 아니라 사고다.
- 텍스트 블록을 그리드의 2/3 지점에서 끊고 나머지를 비워두기
- 이미지를 화면 밖으로 흘려보내기(
full을 넘어margin-inline: calc(var(--space-4) * -1)) - 제목과 본문의 시작선을 일부러 어긋내기 — 단, 같은 어긋남을 페이지 전체에서 반복할 때만
벤토 그리드 주의
벤토(크기가 다른 카드들의 격자)는 정보를 묶거나 우선순위를 드러낼 때 쓸 수 있다. 카드 크기 변화만으로 고유성·스크롤 깊이·이탈을 예측할 수는 없다. 아래 장치는 선택지이며, 구조가 필요로 할 때만 쓴다:
- 카드 경계를 없애고 여백만으로 구획
- 그리드에서 한 칸을 의도적으로 비우기
- 카드 하나만 규칙을 깨고 밖으로 나가기
2. 시선 흐름
사람은 페이지를 읽지 않고 훑는다. 훑는 경로를 설계하는 것이 레이아웃의 본체다.
- 첫 화면이 대표 과업에 필요한 정체성·가치·다음 행동을 충분히 설명하는지 확인한다. 세 요소는 흔한 점검 틀이지 모든 화면의 최대 항목 수가 아니다
- 위계는 크기·굵기·색·여백·위치의 조합으로 만든다. 어떤 두 변수를 반드시 바꿔야 한다는 공식보다, 회색조와 실제 콘텐츠에서 우선순위가 읽히는지를 본다
- 시선을 한 번은 멈춰라 — 전부 같은 리듬으로 흐르면 아무것도 강조되지 않는다. 섹션 하나는 리듬을 깨야 한다
- 스크롤 뒤에도 새 정보나 과업 진전이 있는지 확인한다. 반복 카드가 이탈을 만든다는 보편 증거는 없으므로, 실제 콘텐츠와 사용자 행동으로 판단한다
의미가 다르면 섹션 토폴로지도 달라야 한다
일관성은 같은 거시 그리드를 복제하는 것이 아니다. 연속한 섹션의 역할이 다른데 모두 왼쪽 큰 제목 + 오른쪽 목록이면 구조 슬롭이다. 색·배경·카드 radius를 바꿔도 읽는 동작은 같다.
2단계에서 섹션 역할과 토폴로지를 나란히 적는다.
| 역할 | 맞는 토폴로지 예 |
|---|---|
| 장소·관계 | 지도/도식 + 접근 가능한 목록 쌍둥이 |
| 비용·사양 | 원장·명세서·비교표 |
| 절차·판정 | 흐름·분기·상태 전이 |
| 이미지·재질 | 풀블리드·필름스트립·캡션 |
| 입력·결과 | 폼 + 결과 인스펙터 |
다르게 보이기 위해 억지로 바꾸지는 마라. 같은 데이터 묶음을 반복하는 목록은 같은 구조가 맞다. 규칙은 의미 역할이 다르면 읽는 동작도 달라야 한다다. 연속 3개 섹션의 DOM 골격과 회색조 실루엣이 같다면, 그중 둘의 정보 구조부터 다시 설계한다.
같은 액션 행의 직접 자식은 클릭 박스 중심을 잰다
한 액션 묶음의 같은 행에 놓인 직접 자식인 채운 버튼과 텍스트 링크는 align-items:center만으로 시각 중심이 맞지 않을 수 있다. 중첩된 아이콘·배지까지 한꺼번에 비교하거나, 모바일에서 세로로 쌓인 액션에 이 규칙을 적용하면 오탐이다.
- 두 액션의 실제 hitbox와 정렬을 함께 측정한다. 44px은 편안한 시작값일 수 있지만, WCAG 2.2 AA의 포인터 target 최소 기준은 예외를 포함해 24×24 CSS px다 (
evidence-ledger.md의WCAG-TARGET) getBoundingClientRect()의 중심 y 차이가 브라우저 CSS 픽셀 기준 2px 이하여야 한다- 텍스트 baseline이 아니라 클릭 가능한 박스 전체를 비교한다
- 보조 링크의 hitbox를 키운 결과 밀도가 나빠지면, 행동 중요도·오입력·레이아웃을 보고 세로 배치나 다른 구조를 검토한다
패널을 숨기면 그리드 트랙도 회수한다
데스크톱 목록+인스펙터 분할 작업대에서 패널만 display:none으로 숨기고 2열 grid-template-columns를 유지하면 큰 빈 면이 남는다. 닫힌 상태는 가시성 변화가 아니라 레이아웃 상태 변화다. 모바일에서 인스펙터가 시트·전체 화면으로 열리는 구조에는 데스크톱 2px 트랙 계약을 그대로 적용하지 않고, 시트 닫힘·배경 복귀·포커스 복원을 별도로 잰다.
.workbench { grid-template-columns: minmax(0, 1fr) minmax(20rem, .55fr); }
.workbench.is-inspector-closed { grid-template-columns: minmax(0, 1fr); }
.workbench.is-inspector-closed .inspector { display: none; }
닫힘 상태에서 목록 오른쪽 끝과 작업대 오른쪽 끝의 차이를 2px 이하로 재고, 행 선택 뒤 2열이 복원되는 것도 함께 단언한다.
3. 타이포그래피
토큰은 3단계에서 정했다. 여기서는 적용이다.
폰트 자체의 로딩·기능·폴백은 references/typography.md 를 보라.
위계는 3단계면 충분하다
디스플레이 / 섹션 제목 / 본문. h4, h5, h6 까지 시각적으로 구분하려 들면 위계가 무너진다. 필요하면 웨이트나 색으로 구분해라, 새 크기를 만들지 말고.
본문이 주인공이다
헤드라인은 눈에 띄기 쉽다. 본문이 읽히는지가 실력이다.
- 한 줄 길이(
--measure)를 본문 역할에 적용하고, 실제 언어·글꼴·확대 상태에서 읽기 리듬을 검토한다. 전체 폭이 항상 실패인 것은 아니지만 긴 산문에 무심코 적용하지 않는다 - 문단 간격은 줄간격의 1.5배 이상. 들여쓰기와 문단 간격을 동시에 쓰지 마라
- 링크는 색만으로 구분하지 마라(밑줄 또는 다른 신호 병행)
한글이 들어가면
references/antipatterns.md 의 한글 조판 섹션을 읽어라. 요약:
word-break: keep-all— 없으면 단어가 아무 데서나 잘린다- line-height 1.6~1.8 (라틴보다 넉넉하게)
- 자간은 글꼴·크기·스크립트별로 실제 렌더를 보고 조정한다. 한글 음수 자간은 기본 처방으로 쓰지 않으며, 적용했다면 작은 크기·확대·줄바꿈에서 검증한다
- 한 줄 25~40자
- 라틴 폰트를 폴백 스택 앞에 둔다 (숫자·영문이 한글 폰트로 렌더되면 조악해진다)
- 한글 폰트를 지정하지 않는 것 자체가 완성도 미달 신호다
4. 반응형
브레이크포인트가 아니라 콘텐츠에서 시작한다
/* 나쁨: 기기 크기를 가정 */
@media (min-width: 768px) { }
/* 좋음: 콘텐츠가 깨지는 지점 */
.cards { grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); }
auto-fit + minmax는 유동 반복 grid에 유용하다. 미디어쿼리와 container query는 구조·밀도·행동이 바뀌는 실제 실패 지점에서 선택한다.
모바일에서 먼저 확인해라
데스크톱에서 아름다운 것이 모바일에서 무너지는 것이 기본이고, 그 반대는 드물다. 특히:
- 큰 타이포는 모바일에서 줄 수·가려짐·다음 행동의 가시성을 보고 크기·폭·문구·구도를 함께 조정한다. 단순 축소가 유일한 해법은 아니다
- 가로 스크롤이 생기는 요소를 찾아라 (
overflow-x: hidden으로 덮지 말고 원인을 고쳐라) - 터치 타깃은 과업 빈도·오입력 위험과 WCAG 2.2 AA의 24×24 CSS px 최소 기준(예외 포함)을 함께 검토한다
- 호버로만 접근되는 기능을 만들지 마라
4-b. 모바일 내비 — 햄버거를 기본값으로 삼지 마라
내비 구조는 섹션 수가 아니라 대표 과업, 라벨 길이, 우선순위, locale, 화면 폭을 함께 보고 0단계에서 정한다. Hick의 연구를 항목 수 공식으로 쓰지 않는다.
| 관찰한 조건 | 검토할 구조 | 검증 |
|---|---|---|
| 모든 우선 행동이 한 줄에서 읽히고 hitbox가 확보됨 | 직접 노출된 내비 | 작은 폭·키보드에서 라벨 접힘과 순서를 본다 |
| 라벨이 길거나 행동 우선순위가 갈림 | 일부 노출 + 추가 메뉴 또는 하단 행동 | 자주 쓰는 행동의 탐색 시간·오입력을 본다 |
| 문맥별 명령이 많음 | 컨텍스트 메뉴·검색·계층적 IA | 메뉴 열기/닫기·포커스·Esc·현재 위치를 시험한다 |
항목 수 대신 실제 라벨·과업으로 결정한다
워드마크와 내비를 한 줄에 두면 둘 다 접힌다. 좁은 화면에서는 위아래로 쌓아라.
@media (max-width: 560px) {
.site-head { flex-direction: column; align-items: stretch; gap: var(--space-3); }
.nav { flex-wrap: nowrap; justify-content: space-between; letter-spacing: 0.04em; }
}
라벨 자간(0.14em)이 좁은 화면에서 폭을 크게 먹는다. 자간부터 줄여라 —
폰트 크기를 줄이는 것보다 읽기에 덜 해롭다.
단순 공개/접기에는 <details>를 검토한다
<details>는 간단한 공개/접기에는 유용하지만, 복잡한 메뉴의 포커스 이동·Esc·모달 동작을 자동으로 완성하지 않는다. 실제 키보드와 보조기술에서 목표 행동을 시험하고, 필요한 패턴을 선택한다.
<details class="nav-mobile">
<summary aria-label="메뉴">≡</summary>
<div class="nav-sheet"><a href="#a">…</a></div>
</details>
.nav-mobile > summary { list-style: none; cursor: pointer; }
.nav-mobile > summary::-webkit-details-marker { display: none; }
.nav-sheet { position: absolute; right: 0; }
아이콘이 세 줄(≡)일 필요는 없다. 에디토리얼이면 "메뉴" 라고 적는 편이 낫다 — 글자는 뜻이 분명하고, 세 줄은 학습된 관례일 뿐이다.
어느 쪽이든 검사는 같다
항목의 top 값이 한 종류여야 한다. 높이만 봐서는 못 잡는다(→ antipatterns.md).
new Set([...document.querySelectorAll('.nav a')]
.map(e => Math.round(e.getBoundingClientRect().top))).size === 1
4-c. 구조를 바꾸면 미디어쿼리도 같이 다시 써라
실측 사고. 히어로를 12칸 그리드에서 "글 + 사진 컨테이너" 2단으로 바꾸면서
좁은 화면 규칙을 그대로 뒀다. .hero-text 가 auto 1fr 2칸인데
세로 라벨을 가로로 눕히자 그 긴 문장이 auto 칸을 통째로 먹어 h1 칸이 짜부라졌고,
헤드라인이 390px 에서 6줄, 320px 에서 11줄이 됐다.
넓은 화면에서는 멀쩡했다. 구조 변경의 대가는 항상 좁은 화면에서 먼저 청구된다.
규칙: 그리드 구조를 바꿨으면 그 자리에서 320·390·768 을 다시 재라. 나중에 하면 원인이 어느 변경이었는지 못 찾는다.
4-d. 초광폭은 여백이 아니라 별도 레이아웃 상태다
1440px에서 멀쩡한 페이지가 1920·2560px에서 깨지는 방식은 반대다. 모바일처럼 넘치지는 않지만, 본문은 끝없이 늘어나고 고정 폭 헤드라인은 빈 여백에 밀려나며 표는 너무 좁아진다. 컨테이너 하나를 무조건 풀거나 조이지 마라. 읽는 표면과 비교·명세 표면의 요구가 다르다.
:root { --measure: 42rem; --container-wide: 74rem; }
.prose { max-width: var(--measure); }
.specification { width: min(100% - 2 * var(--pad-inline), var(--container-wide)); }
@media (min-width: 120rem) {
.specification { --container-wide: 86rem; } /* 데이터 표면만 확장 */
}
- 본문·설명·폼은
--measure를 지켜 행 길이와 label-입력 관계를 읽을 수 있게 둔다 - 표·도면·비교 작업대는 더 넓어도 되지만, 어떤 surface가 넓어지는지 이름으로 구분한다. 전역
max-width해제는 해결이 아니다 - 1920과 2560에서 h1 폭·히어로 칼럼·table/flex 셀의 실제 rect를 재고, 빈 공간이 필요한지와 내용 폭이 부족한지를 별도로 판정한다
Apple도 다양한 화면 크기에서 적응형 레이아웃과 읽기 좋은 텍스트 폭의 제한을 권한다. 여기의 정확한 수치는 제품 토큰으로 정하고, 넓은 폭을 시험하지 않은 상태를 통과로 부르지 않는다. Apple HIG Layout
4-e. 분할 히어로의 사진에는 상한이 있고, 카피에는 최소 폭이 있다
초광폭에서 1fr 1fr을 그대로 두면 사진은 창과 함께 계속 커지는데 카피는 상대적으로 가늘어져, 화면의 절반을 차지한 이미지와 읽을 수 없는 작은 설명이 공존한다. 풀블리드 편집 사진이 의도적으로 화면을 점유하는 경우가 아니라면, 분할 히어로를 wide surface처럼 별도 계약으로 둔다.
@media (min-width: 120rem) {
.hero { width: min(100%, var(--stage-wide)); margin-inline: auto; }
.hero-copy { width: min(100%, var(--copy-wide)); justify-self: end; }
.hero-media { width: min(100%, var(--media-wide)); justify-self: end; }
}
--stage-wide,--copy-wide,--media-wide, 후속--workbench-wide를 따로 이름 짓는다. 한--wrap을 무한히 키우지 않는다- 1920·2560에서 hero·copy·media·다음 정보 레일의 실제 rect를 기록한다. media의 상한, copy의 최소 폭, hero의 중앙/의도된 정렬을 그 페이지의 계약으로 단언한다
- 이미지가 넓어질 자격은 이미지 자체의 편집적 역할에 있다. 정보·주문·비교가 뒤따르는 제품 화면에서는 후속 레일도 같은 밀도로 확장해, 첫 장면만 거대하고 본문은 점처럼 보이는 단절을 만들지 않는다
- 이 계약은 hero만의 면허가 아니다. 과정·추천·가맹 같은 별도 text-media split은 각각 원래의 padding/
max-width규칙을 가진다. wide에서 새 stage를 넣을 때는 inheritedpadding-inline의 양쪽 값과 상위 container selector의 specificity를 함께 확인한다. 한쪽에calc((100vw - --wrap) / 2)가 남으면 grid rect는 멀쩡해도 실제 텍스트 폭이 한 글자까지 붕괴할 수 있다. 각 split마다 heading의 실제 폭·줄 수와 copy/media rect를 별도로 측정한다.
이 규칙은 읽기 폭을 지키는 기존 wide-surface 원칙의 구체형이다. 사람의 시선은 이미지 크기보다 이미지·카피·행동의 비례에서 우선순위를 읽는다.
5. 이 단계의 통과 조건
- CSS/HTML만으로 페이지가 완성됐다. JS를 꺼도 읽힌다
- 모든 값이 3단계 토큰에서 나온다. 하드코딩된 px/색이 없다
- 첫 화면에 메시지가 셋 이하다
- 의미 역할이 다른 연속 섹션이 같은 거시 그리드를 3회 반복하지 않는다. 반복 데이터면 같은 구조를 유지한 이유가 적혀 있다
- 본문에
--measure가 적용됐다 - 모바일 폭 320px(iPhone SE 세로)에서 가로 스크롤이 없다
- 키보드 Tab 만으로 모든 인터랙티브 요소에 도달한다. 포커스 링이 보인다
- 같은 행 액션 묶음의 직접 자식 hitbox가 과업·WCAG target 기준에 맞고, 중심 y 차이를 실제 CSS 픽셀로 기록했다
- 데스크톱 분할 패널을 숨긴 상태에서 빈 그리드 트랙이 남지 않고 주 표면이 전체 폭을 회수한다. 모바일 시트는 닫힘·배경·포커스 복원을 따로 검증한다
- 한글이 있다면 조판 규칙이 적용됐다
근거: research/references/02-methodology.md, 03-trends-2026.md, 04-ai-slop-signatures.md (조사일 2026-08-20)