# layout — 레이아웃과 타이포그래피
4-1 단계에서 읽는다. **이 단계가 끝나면 아무 이펙트 없이도 완성된 페이지**여야 한다. 그것이 이후 모든 폴백의 기반이다.
접근성 규범(포커스·키보드·라이브 리전·히트 영역)은 [accessibility.md](accessibility.md), 입력 촉감·제스처·스프링은 [interaction-feel.md](interaction-feel.md), 아이콘 정렬·상태·RTL 뒤집기는 [icons.md](icons.md), 인쇄·이메일 적응은 [print-email.md](print-email.md)에서 각각 다룬다. 이 문서는 그리드·시선 흐름·타이포그래피 적용·반응형 구조를 다룬다.
---
## 이 문서를 읽는 법
| 상황 | 읽을 곳 |
|---|---|
| 그리드·정렬선·읽기 폭을 정한다 | §1 |
| 시선 흐름·그루핑·주요 액션·좋은 상태 신호 | §2 |
| 섹션마다 어떤 토폴로지를 쓸지 정한다 | §2 "의미가 다르면 섹션 토폴로지도 달라야 한다" |
| **히어로 구도를 고른다**(분할은 여덟 가운데 하나) · 표제 위 라벨·표제 꼴·리드·행동 | §2-b |
| **내비 형태를 고른다**(데스크톱 포함, 고정·흐림 여부) | §2-c → 좁은 화면 구현은 §4-b |
| **푸터에 무엇을 담고 어떤 형태로 둘지** | §2-d |
| 타이포를 적용한다(한글이면 정본은 [typography.md](typography.md) §6-5) | §3 |
| 반응형·입력 방식·컨테이너 쿼리·카드 안쪽 정렬선(subgrid) | §4 |
| **화면·페이지가 둘 이상이다**(만드는 순서·공통 셸·감사 범위) | §4-m |
| 모바일 내비 구현(쌓기·접기·검사) | §4-b |
| 구조를 바꿨다 | §4-c |
| 1920·2560 초광폭 | §4-d. 분할 히어로를 골랐으면 §4-e도 |
| 콘솔·전문가 도구 | §4-f, §4-h, §4-i |
| 표 | 데이터 테이블 정본은 [component-systems.md](component-systems.md) §11. 카드 변환 구현은 §4-g |
| RTL·다국어·로케일 포맷 | §4-j, §4-k, §4-l |
| 이 단계를 마친다 | §5 |
---
## 1. 그리드를 먼저 정한다
### 공통 정렬선과 역할별 폭
```css
.page {
display: grid;
grid-template-columns:
[full-start] minmax(var(--space-6), 1fr)
[wide-start] minmax(0, 12rem)
[main-start] minmax(0, var(--measure)) [main-end] /* 3단계에서 정한 값이다 */
minmax(0, 12rem) [wide-end]
minmax(var(--space-6), 1fr) [full-end];
}
.page > * { grid-column: main; }
.page > .wide { grid-column: wide; }
.page > .full { grid-column: full; }
```
`main`의 읽기 폭은 3단계에서 언어와 폰트에 맞춰 정한 `--measure` 토큰으로 둔다. `60rem` 같은 값이 그 본문 조건을 넘으면 대표 문단의 줄 길이와 확대 상태에서 다시 검증한다. `wide`·표·작업대는 별도 역할 토큰과 실제 사용 장면으로 검증한다.
`ch`는 선택한 글꼴의 메트릭에 의존한다. 한글과 라틴의 실제 줄 길이는 글꼴·언어·내용으로 달라지므로, 예시 폭을 보편 수치로 쓰지 말고 대표 문단을 렌더해 확인한다.
이 공통 레일은 **본문 폭 · 넓은 블록 · 전체 폭**의 정렬 기준이다. 본문 읽기 폭, 증거·표, 작업대는 역할별 컨테이너 토큰(`--measure`, `--container-wide`, `--workbench-wide`)을 써도 된다. 각 역할의 폭·padding을 토큰으로 이름 붙이고 공통 레일과의 관계를 렌더에서 확인한다.
### 비대칭을 두려워하지 마라
중앙 정렬과 비대칭은 모두 선택지다. 초점, 균형, 내용 군집, 작은 폭에서의 읽기를 실제 콘텐츠로 비교해 고른다. 비대칭에도 규칙이 있어야 한다 — 아무 데나 놓는 것은 비대칭이 아니라 사고다.
- 텍스트 블록을 그리드의 2/3 지점에서 끊고 나머지를 비워두기
- 이미지를 화면 밖으로 흘려보내기(`full` 을 넘어 `margin-inline: calc(var(--space-6) * -1)`)
- 제목과 본문의 시작선을 일부러 어긋내기 — 단, **같은 어긋남을 페이지 전체에서 반복**할 때만
### 벤토 그리드 주의
벤토(크기가 다른 카드들의 격자)는 정보를 묶거나 우선순위를 드러낼 때 쓸 수 있다. 카드 크기 변화만으로 고유성·스크롤 깊이·이탈을 예측할 수는 없다. 아래 장치는 선택지이며, 구조가 필요로 할 때만 쓴다:
- 카드 경계를 없애고 여백만으로 구획
- 그리드에서 한 칸을 의도적으로 비우기
- 카드 하나만 규칙을 깨고 밖으로 나가기
### 선택자 특정성은 조용히 상쇄된다
**관찰 후보 — 엔지니어링 관행.** 상위 컨테이너 클래스(`.section`)와 하위 컴포넌트 클래스(`.cta`)는 특정성이 같아(0,1,0) 로드 순서가 이긴다. `section.hero`(0,1,1)처럼 특정성이 다르면 로드 순서와 무관하게 한쪽이 이긴다. 4-e의 초광폭 분할 히어로 사례(상위 container selector와 wide stage의 `padding-inline` 중 한쪽만 남아 텍스트 폭이 붕괴한 것)는 이 원칙의 구체형이다. 4단계에서 새 컴포넌트 선택자를 쓸 때는 그 선택자가 덮으려는 상위 선택자의 특정성과, 두 선택자가 같은 속성을 정의하는지를 함께 확인한다. [SKILL-FRONTEND-DESIGN]
---
## 2. 시선 흐름
사람은 페이지를 읽지 않고 **훑는다.** 훑는 경로를 설계하는 것이 레이아웃의 본체다.
1. 첫 화면이 대표 과업에 필요한 정체성·가치·다음 행동을 충분히 설명하는지 확인한다. 세 요소는 흔한 점검 틀이지 모든 화면의 최대 항목 수가 아니다
2. 위계는 크기·굵기·색·여백·위치의 조합으로 만든다. 어떤 두 변수를 반드시 바꿔야 한다는 공식보다, 회색조와 실제 콘텐츠에서 우선순위가 읽히는지를 본다
3. 강조하거나 역할을 전환해야 할 때는 리듬·크기·여백·색의 차이로 시선을 머물게 할 수 있다. 모든 섹션의 리듬을 일부러 깨야 하는 것은 아니다
4. 스크롤 뒤에도 새 정보나 과업 진전이 있는지 확인한다. 반복 카드가 이탈을 만든다는 보편 증거는 없으므로, 실제 콘텐츠와 사용자 행동으로 판단한다
**좋은 상태의 관찰 가능한 신호.** 실패 목록만으로는 무엇이 잘된 것인지 판정할 수 없다. 아래는 시작 신호이지 통과선이 아니다 — 하나라도 빠지면 그 이유를 말할 수 있어야 한다.
| 신호 | 보는 법 |
|---|---|
| 회색조 캡처에서 읽는 순서(1→2→3)가 의도와 같다 | 첫 화면을 회색조로 바꿔 짧게 보고 먼저 읽힌 것을 적는다 |
| 한 화면의 주요 시작선(정렬선)이 셋 이하다 | 브랜드·제목·본문·카드의 왼쪽 x값을 잰다 |
| 섹션 간격의 변주가 정보 전환과 일치한다 | 간격이 커지는 자리가 실제로 주제가 바뀌는 자리인지 본다 |
| 본문 문단의 회색 농도가 고르고, 강·약 리듬이 두 단계 이상 있다 | 흐리게 본 화면에서 덩어리가 고르게 회색인지, 강조가 점처럼 찍히는지 본다 |
| 제목과 본문의 차이가 크기·굵기·색·여백 중 둘 이상에서 난다 | 계산된 스타일을 나란히 적는다 |
### 그루핑 도구에는 선호 순서가 있다
**관찰 후보 — 시작값.** 관련 요소를 묶을 때는 이 순서로 검토한다.
1. **네거티브 스페이스** — 기본값. 관련 항목은 가깝게, 무관한 항목은 멀게 둔다.
2. **배경 도형** — 카드·채워진 컨테이너. 그룹이 반드시 하나의 단위로 읽혀야 할 때(선택 가능한 행, 드래그 가능한 카드 등)만 쓴다.
3. **구분선** — 최후 수단. 공간이 너무 비싼 밀집 데이터(표, 긴 설정 목록)에서만 쓴다. 헤어라인 두께, 낮은 대비로 두고, 이미 공간이 구조를 만들었다면 큰 gap과 중복해서 쓰지 않는다.
그룹 내 간격과 그룹 간 간격의 비율은 **1:2를 시작값**으로 둔다 — 그룹 내 `--space-2`(8px)면 그룹 간은 `--space-4`(16px) 이상. 비율이 이보다 좁으면 눈이 그룹 경계를 못 잡고 노이즈로 읽는다. 프로젝트 spacing 스케일에 이미 값이 있으면 그것을 그대로 쓰고, 이 비율로 다시 맞추지 않는다. [SKILL-BETTER-LAYOUT]
### 결정 단위당 주요 액션은 하나다
**관찰 후보.** 첫 화면은 목차이지 책 전체가 아니다. **결정 단위**(히어로 하나, 폼 하나, 다이얼로그 하나, 반복 모듈 하나)마다 주요 액션을 하나로 정하고(색으로 표시하는 방법은 [color.md](color.md) §7), 보조 액션이 2~3개를 넘으면 메뉴 뒤로 묶는다. 레벨 1에서 모든 것을 보여주는 긴 뷰보다, 더 깊이 링크하는 짧은 뷰를 우선한다. [SKILL-BETTER-LAYOUT]
- 상품 목록의 카드별 "담기", 요금제 열마다 있는 가입 버튼처럼 **반복 모듈은 모듈마다 하나**가 정상 관례다. 요금제는 추천 열 하나만 채우고 나머지는 외곽선으로 두는 것을 시작값으로 한다.
- 구매/정기구독처럼 결과가 다른 두 경로는 "세그먼트 선택 + 단일 CTA"와 "병렬 두 CTA" 중 브리프의 목표 행동 우선순위로 고른다. 둘 다 정당하다.
### 의미가 다르면 섹션 토폴로지도 달라야 한다
일관성은 같은 거시 그리드를 복제하는 것이 아니다. **연속한 섹션의 역할이 다른데 모두 `왼쪽 큰 제목 + 오른쪽 목록`이면 구조 슬롭**이다. 색·배경·카드 radius를 바꿔도 읽는 동작은 같다.
2단계에서 섹션 역할과 토폴로지를 나란히 적는다.
역할마다 후보가 셋 이상 있다. 칸 안의 순서에는 뜻이 없다 — 첫 번째 것을 고르지 않는다. 고르는 기준은 오른쪽 열이다.
| 역할 | 토폴로지 후보 | 무엇이 고르나 |
|---|---|---|
| 장소·관계 | 지도 + 같은 내용의 목록 / 오는 길을 사진 순서로 / 영업시간·위치 표 | 처음 오는 사람인가, 아는 사람이 시간만 확인하는가 |
| 비용·사양 | 나란히 놓는 비교표 / 조건을 넣으면 값이 나오는 계산기 / 항목과 합계가 있는 견적서 한 장 / 단계별 견적 | 여러 안을 비교하는가, 자기 조건의 값 하나를 구하는가 |
| 절차·판정 | 타임라인 / 하루 일과표 / 전후 사진 쌍 / 체크리스트 / 갈림이 있는 흐름도 | 순서가 중요한가, 결과가 중요한가, 갈림이 있는가 |
| 이미지·재질 | 풀블리드 한 장 / 필름스트립 / 크기가 다른 사진 묶음 / 확대 컷 + 주석 | 사진이 몇 장이고 그중 주인공이 있는가([images.md](images.md) §2 처리 표) |
| 입력·결과 | 폼 + 결과 인스펙터 / 입력 한 칸과 바로 나오는 답 / 단계형 입력 | 입력 항목 수, 결과를 보면서 고치는가 |
| 신뢰 | 사람 사진과 이름 / 작업 과정 사진 / 실제 문서 사본 / 숫자 하나를 크게 | 실제로 가진 증거가 무엇인가(없는 후기·수치는 만들지 않는다) |
| 기록·목록 | 날짜순 목록 / 카드 격자 / 표 / 달력 | 훑는가, 찾는가, 열끼리 비교하는가 |
**표와 "원장·장부"는 기본값이 아니다.** 쇼케이스 19종 계측(2026-10-06)에서 12종이 `
`을 썼고 8종에 "원장·장부·ledger"라는 낱말이 있었다. 이 표의 비용·사양 행에 "원장·명세서·비교표" 한 줄만 있었던 탓이다. 표는 여러 행을 열끼리 비교할 때 쓴다. 섹션 이름에 "원장"·"장부"를 쓰려면 그 업종이 실제로 쓰는 낱말인지 확인한다. 배경색만 번갈아 바꾼 띠는 토폴로지가 아니다. 장르별 섹션 흐름 예는 [genre-conventions.md](genre-conventions.md)에 있다.
**다르게 보이기 위해 억지로 바꾸지는 마라.** 같은 데이터 묶음을 반복하는 목록은 같은 구조가 맞다. 규칙은 `의미 역할이 다르면 읽는 동작도 달라야 한다`다. 연속 3개 섹션의 DOM 골격과 회색조 실루엣이 같다면, 그중 둘의 정보 구조부터 다시 설계한다.
### 같은 액션 행의 직접 자식은 클릭 박스 중심을 잰다
한 액션 묶음의 **같은 행에 놓인 직접 자식**인 채운 버튼과 텍스트 링크는 `align-items:center`만으로 시각 중심이 맞지 않을 수 있다. 중첩된 아이콘·배지까지 한꺼번에 비교하거나, 모바일에서 세로로 쌓인 액션에 이 규칙을 적용하면 오탐이다.
- 두 액션의 실제 hitbox와 정렬을 함께 측정한다. 터치 타깃 기준(24px 하드 게이트와 44px 계약의 두 층)은 [accessibility.md](accessibility.md) §8이 정본이다
- `getBoundingClientRect()`의 중심 y 차이가 브라우저 CSS 픽셀 기준 2px 이하여야 한다
- 텍스트 baseline이 아니라 클릭 가능한 박스 전체를 비교한다
- 보조 링크의 hitbox를 키운 결과 밀도가 나빠지면, 행동 중요도·오입력·레이아웃을 보고 세로 배치나 다른 구조를 검토한다
### 패널을 숨기면 그리드 트랙도 회수한다
데스크톱 목록+인스펙터 분할 작업대에서 패널만 `display:none`으로 숨기고 2열 `grid-template-columns`를 유지하면 큰 빈 면이 남는다. 닫힌 상태는 가시성 변화가 아니라 **레이아웃 상태 변화**다. 모바일에서 인스펙터가 시트·전체 화면으로 열리는 구조에는 데스크톱 2px 트랙 계약을 그대로 적용하지 않고, 시트 닫힘·배경 복귀·포커스 복원을 별도로 잰다.
```css
.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열이 복원되는 것도 함께 단언한다.
### 컨트롤은 정적 텍스트와 다르게 보여야 한다
**관찰 후보 — 양방향.** 모든 상호작용 요소는 배경·테두리·일관된 배치 구역 중 하나로 정적 텍스트와 구별되어야 한다. 주변 텍스트와 똑같이 스타일된 컨트롤은 컨트롤로 읽히지 않는다. 역방향 함정도 같은 비중으로 본다 — 옆의 진짜 버튼과 똑같이 생긴 클릭 불가 배지·라벨은 헛클릭을 모은다. [SKILL-BETTER-LAYOUT]
### 광학 정렬
아이콘이 있는 버튼, 재생 삼각형, 비대칭 아이콘처럼 기하학적 중심은 맞아도 눈에는 어긋나 보이는 보정은 [icons.md](icons.md) §6을 본다.
### 점진적 공개에는 눈에 보이는 단서가 필요하다
**관찰 후보 — 시작값.** 숨긴 콘텐츠에 단서가 없으면 없는 것과 같다. 프로젝트에 기존 disclosure 패턴이 있으면 그것을 쓰고, 없을 때만 아래 레시피를 시작값으로 검토한다.
- **피킹 아이템**: 가로 스크롤러·캐러셀에서 다음 카드가 컨테이너 엣지 너머로 `16px`~`32px` 삐져나오게 한다.
- **디스클로저 컨트롤**: 접힌 섹션에는 쉐브론이나 "더 보기"류 컨트롤을 두되, 숨겨진 개수를 라벨에 명시한다 — 예: "결과 12개 더 보기". 개수 없는 "더 보기"만으로는 약하다.
- **잘림 단서**: 클램프된 텍스트는 말줄임표와 함께 전체 값에 도달할 수단(펼치기·링크·툴팁)을 같이 둔다.
```css
.scroller {
--peek: 24px; /* 다음 카드가 비치는 폭 — 시작값 16~32px, 실측 조정 */
display: flex;
gap: var(--space-2);
overflow-x: auto;
padding-inline: var(--space-6);
scroll-padding-inline: var(--space-6);
scroll-snap-type: x mandatory;
}
.scroller > * {
/* 100% 는 패딩을 뺀 콘텐츠 폭이다. 오른쪽 패딩 영역에도 다음 카드가 비치므로 그만큼 덜 뺀다 */
flex: 0 0 calc(100% - var(--space-2) - max(0px, var(--peek) - var(--space-6)));
scroll-snap-align: start;
}
```
실제 렌더에서 피크 폭이 16~32px인지 확인한다.
[SKILL-BETTER-LAYOUT]
### 표의 숫자는 끝에, 텍스트는 시작에 정렬한다
**관찰 후보.** 정렬 기준선의 작은 집합을 고르고 고수한다. 표 안에서 숫자는 트레일링 엣지에, 텍스트는 리딩 엣지에 정렬한다(데이터 테이블 전체 규칙은 [component-systems.md](component-systems.md) §11) — 숫자 자체의 자릿수 정렬(`tabular-nums`)은 별개 축이며 [typography.md](typography.md)를 따른다. 물리적 left/right 대신 리딩/트레일링으로 사고하는 이유는 4-j "방향 독립 레이아웃" 절을 본다. [SKILL-BETTER-LAYOUT]
### 구체적인 내비 라벨이 우산 용어보다 낫다
**관찰 후보.** 내비게이션이나 섹션 라벨을 지을 때 "홈"·"메뉴" 같은 우산 용어보다, 그 화면이 실제로 하는 일을 가리키는 구체적 이름("진행 상황"·"보관함")이 더 잘 읽힌다. 구체적 라벨은 클릭 전에 무엇을 보게 될지 예측하게 한다. [SKILL-APPLE-DESIGN]
### 모달 액션 행은 스크롤 영역과 분리한다
**프로젝트 계약.** 리사이즈 가능한 패널 바닥, 고정 높이 모달의 폴드 아래, 확장하는 키보드 뒤처럼 잘릴 수 있는 자리에 확인·취소 같은 핵심 액션을 두지 않는다. 모달 콘텐츠가 스크롤되면 액션 행은 함께 스크롤되지 않고 안정된 자리(스티키 footer 또는 뷰 상단)에 남는다. native `