# layout — 레이아웃과 타이포그래피 4-1 단계에서 읽는다. **이 단계가 끝나면 아무 이펙트 없이도 완성된 페이지**여야 한다. 그것이 이후 모든 폴백의 기반이다. --- ## 1. 그리드를 먼저 정한다 ### 하나의 그리드에서 모든 것이 나온다 ```css .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 를 넘어가 이 문서의 통과 조건을 스스로 어긴다. 한글은 보통 라틴보다 좁다(35ch ≈ 561px vs 640px). 이 한 번의 정의로 **본문 폭 · 넓은 블록 · 전체 폭**이 전부 정렬된다. 섹션마다 `max-width` 와 `padding` 을 다시 쓰면 반드시 어긋난다. ### 비대칭을 두려워하지 마라 12열 중앙 정렬은 안전하고 잊힌다. 실제로 기억되는 레이아웃은 **의도적으로 한쪽으로 몰려 있다**. 다만 비대칭에도 규칙이 있어야 한다 — 아무 데나 놓는 것은 비대칭이 아니라 사고다. - 텍스트 블록을 그리드의 2/3 지점에서 끊고 나머지를 비워두기 - 이미지를 화면 밖으로 흘려보내기(`full` 을 넘어 `margin-inline: calc(var(--space-4) * -1)`) - 제목과 본문의 시작선을 일부러 어긋내기 — 단, **같은 어긋남을 페이지 전체에서 반복**할 때만 ### 벤토 그리드 주의 벤토(크기가 다른 카드들의 격자)는 **더 이상 차별화가 아니라 새 기본값**이다. 효과는 있지만(스크롤 깊이 증가) 표준화가 끝나서 "고민 안 했음"의 신호로 읽힌다. 쓰려면 최소한 다음 중 하나는 해라: - 카드 경계를 없애고 여백만으로 구획 - 그리드에서 한 칸을 의도적으로 비우기 - 카드 하나만 규칙을 깨고 밖으로 나가기 --- ## 2. 시선 흐름 사람은 페이지를 읽지 않고 **훑는다.** 훑는 경로를 설계하는 것이 레이아웃의 본체다. 1. **첫 화면에서 세 가지만** — 무엇인지 / 왜 좋은지 / 다음 행동. 넷 이상이면 아무것도 안 읽힌다 2. **위계는 크기가 아니라 대비로 만든다** — 크기·굵기·색·여백 중 **둘 이상을 동시에** 바꿔야 위계가 보인다. 크기만 조금 키우는 것은 위계가 아니다 3. **시선을 한 번은 멈춰라** — 전부 같은 리듬으로 흐르면 아무것도 강조되지 않는다. 섹션 하나는 리듬을 깨야 한다 4. **스크롤은 보상이어야 한다** — 다음 화면에 무언가 새로운 것이 있어야 한다. 같은 카드 그리드가 세 번 반복되면 거기서 이탈한다 ### 의미가 다르면 섹션 토폴로지도 달라야 한다 일관성은 같은 거시 그리드를 복제하는 것이 아니다. **연속한 섹션의 역할이 다른데 모두 `왼쪽 큰 제목 + 오른쪽 목록`이면 구조 슬롭**이다. 색·배경·카드 radius를 바꿔도 읽는 동작은 같다. 2단계에서 섹션 역할과 토폴로지를 나란히 적는다. | 역할 | 맞는 토폴로지 예 | |---|---| | 장소·관계 | 지도/도식 + 접근 가능한 목록 쌍둥이 | | 비용·사양 | 원장·명세서·비교표 | | 절차·판정 | 흐름·분기·상태 전이 | | 이미지·재질 | 풀블리드·필름스트립·캡션 | | 입력·결과 | 폼 + 결과 인스펙터 | **다르게 보이기 위해 억지로 바꾸지는 마라.** 같은 데이터 묶음을 반복하는 목록은 같은 구조가 맞다. 규칙은 `의미 역할이 다르면 읽는 동작도 달라야 한다`다. 연속 3개 섹션의 DOM 골격과 회색조 실루엣이 같다면, 그중 둘의 정보 구조부터 다시 설계한다. ### 같은 액션 행의 직접 자식은 클릭 박스 중심을 잰다 한 액션 묶음의 **같은 행에 놓인 직접 자식**인 채운 버튼과 텍스트 링크는 `align-items:center`만으로 시각 중심이 맞지 않을 수 있다. 중첩된 아이콘·배지까지 한꺼번에 비교하거나, 모바일에서 세로로 쌓인 액션에 이 규칙을 적용하면 오탐이다. - 두 액션 모두 `inline-flex; align-items:center; min-height:44px`의 실제 hitbox를 가진다 - `getBoundingClientRect()`의 중심 y 차이가 브라우저 CSS 픽셀 기준 2px 이하여야 한다 - 텍스트 baseline이 아니라 클릭 가능한 박스 전체를 비교한다 - 보조 링크를 44px로 키우면서 빈 padding이 과해지면 액션을 세로로 쌓는다 ### 패널을 숨기면 그리드 트랙도 회수한다 데스크톱 목록+인스펙터 분할 작업대에서 패널만 `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열이 복원되는 것도 함께 단언한다. --- ## 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. 반응형 ### 브레이크포인트가 아니라 콘텐츠에서 시작한다 ```css /* 나쁨: 기기 크기를 가정 */ @media (min-width: 768px) { } /* 좋음: 콘텐츠가 깨지는 지점 */ .cards { grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); } ``` `auto-fit` + `minmax` 로 해결되는 것을 미디어쿼리로 만들지 마라. 미디어쿼리는 **레이아웃 구조 자체가 바뀔 때만** 쓴다. ### 모바일에서 먼저 확인해라 데스크톱에서 아름다운 것이 모바일에서 무너지는 것이 기본이고, 그 반대는 드물다. 특히: - 큰 타이포는 모바일에서 반드시 줄여라 (`clamp` 의 최소값) - 가로 스크롤이 생기는 요소를 찾아라 (`overflow-x: hidden` 으로 덮지 말고 원인을 고쳐라) - 터치 타깃 44×44px 이상 - 호버로만 접근되는 기능을 만들지 마라 --- ## 4-b. 모바일 내비 — 햄버거를 기본값으로 삼지 마라 내비 항목 수는 0단계에서 이미 정해져 있다(= 섹션 수). 구현하다 발견하면 늦다. | 항목 수 | 처리 | 비용 | |---|---|---| | **2~4개** | 한 줄 유지. 자간을 줄이고 `space-between` 으로 편다 | 0 | | **5~6개** | 가로 스크롤 줄, 또는 하단 고정 바 | 낮음 | | **7개 이상** | 여는 메뉴 | `
` 로 JS 0바이트 | **넷인데 햄버거를 쓰면 탭 한 번을 공짜로 뺏는 것이다.** 반대로 여섯을 한 줄에 욱여넣으면 접힌다 — 실측에서 링크가 두 줄(top 46/77)에 걸렸고, 워드마크까지 3줄이 됐다. ### 2~4개 — 쌓는 것이 접히는 것보다 낫다 워드마크와 내비를 한 줄에 두면 둘 다 접힌다. 좁은 화면에서는 **위아래로 쌓아라.** ```css @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`)이 좁은 화면에서 폭을 크게 먹는다. **자간부터 줄여라** — 폰트 크기를 줄이는 것보다 읽기에 덜 해롭다. ### 7개 이상 — `
` 면 JS 가 필요 없다 키보드 조작·`Esc`·포커스 이동이 브라우저 기본으로 온다. 직접 만든 토글은 그걸 전부 다시 구현해야 하고, 대개 빠뜨린다. ```html ``` ```css .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`). ```js 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 을 다시 재라. > 나중에 하면 원인이 어느 변경이었는지 못 찾는다. --- ## 5. 이 단계의 통과 조건 - [ ] CSS/HTML만으로 페이지가 완성됐다. JS를 꺼도 읽힌다 - [ ] 모든 값이 3단계 토큰에서 나온다. 하드코딩된 px/색이 없다 - [ ] 첫 화면에 메시지가 셋 이하다 - [ ] 의미 역할이 다른 연속 섹션이 같은 거시 그리드를 3회 반복하지 않는다. 반복 데이터면 같은 구조를 유지한 이유가 적혀 있다 - [ ] 본문에 `--measure` 가 적용됐다 - [ ] 모바일 폭 **320px**(iPhone SE 세로)에서 가로 스크롤이 없다 - [ ] 키보드 Tab 만으로 모든 인터랙티브 요소에 도달한다. 포커스 링이 보인다 - [ ] 같은 행 액션 묶음의 직접 자식 hitbox가 44px 이상이고 중심 y 차이가 CSS 픽셀 2px 이하다 - [ ] 데스크톱 분할 패널을 숨긴 상태에서 빈 그리드 트랙이 남지 않고 주 표면이 전체 폭을 회수한다. 모바일 시트는 닫힘·배경·포커스 복원을 따로 검증한다 - [ ] 한글이 있다면 조판 규칙이 적용됐다 > 근거: research/references/02-methodology.md, 03-trends-2026.md, 04-ai-slop-signatures.md (조사일 2026-08-20)