designpaca/packages/skill/references/layout.md
Yun Chan b92853bf2b
All checks were successful
ci / build (push) Successful in 23s
release / release (push) Successful in 36s
feat(skill): 색·모바일 내비를 인터뷰에 넣고 이미지 조달을 단계로 승격한다
실제로 한 페이지를 만들어 보고 드러난 것들이다.

0단계 인터뷰에 두 축을 더한다.

- 브랜드 색: 꽃집 작업에서 색을 묻지 않고 레퍼런스 실측 평균으로 정했다.
  결과는 좋았지만 브랜드 색이 있었다면 그걸 무시한 작업이 된다.
  "없다"는 답도 답이고, 그때는 피해야 할 색을 묻는다.
- 좁은 화면 내비: 항목 수는 0단계에 이미 정해져 있는데 구현에서 발견했다.
  2~4개는 쌓고, 5~6개는 가로 스크롤, 7개 이상만 여는 메뉴다.
  넷인데 햄버거를 쓰면 탭 한 번을 공짜로 뺏는 것이다.

4-0 이미지 조달 단계를 새로 만든다(references/images.md).

  자리를 만들기 전에 무엇을 실을지 정한다. codex 설치와 auth_mode 를 확인해
  생성 경로를 쓰고, 생성물이라는 사실을 design.md 에 남긴다.

  래퍼 실패 대응을 기록했다 — codex-cli 0.147.0 은 base64 를 돌려주지 않고
  ~/.codex/generated_images/<session>/exec-<id>.png 로 저장한다. 공식 래퍼는
  "image_gen 이 호출되지 않았다"며 실패하지만 파일은 멀쩡히 있다.
  실측에서 이 메시지를 보고 두 번 다시 만들었고 두 번 다 이미 있었다.

  싣기 전 최적화도 넣었다(실측 10.1MB → 603KB).

구현에서 넘어진 것 셋.

- images.md: <img width height> 를 적으면 그 height 가 계산된 높이가 되어
  aspect-ratio 가 조용히 무시된다. computed 는 "3 / 4" 로 멀쩡히 보이고
  실제 높이는 원본 1448px 이었다. height:auto 한 줄로 10.9 화면이 7 화면이 됐다.
- antipatterns.md: "컨트롤이 2줄로 접히나" 를 개별 높이로 재서 거짓 통과했다.
  링크는 한 줄짜리였고 줄바꿈된 것은 컨테이너였다(top 46/77).
  행 수는 높이가 아니라 top 값의 종류로 센다.
- typography.md: 세로쓰기에 가로쓰기 자간(0.14em)을 물리면 글자 사이 세로
  간격이 되어 낱글자가 흩어진다. 한글은 자소가 모여 한 글자라 특히 심하다.
- layout.md: 그리드 구조를 바꾸면 미디어쿼리도 같이 다시 써야 한다.
  넓은 화면은 멀쩡했고 390px 에서 헤드라인이 6줄이 됐다.
2026-08-21 04:13:03 +09:00

8.7 KiB
Raw Permalink Blame History

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 를 넘어가 이 문서의 통과 조건을 스스로 어긴다. 한글은 보통 라틴보다 좁다(35ch ≈ 561px vs 640px).

이 한 번의 정의로 본문 폭 · 넓은 블록 · 전체 폭이 전부 정렬된다. 섹션마다 max-widthpadding 을 다시 쓰면 반드시 어긋난다.

비대칭을 두려워하지 마라

12열 중앙 정렬은 안전하고 잊힌다. 실제로 기억되는 레이아웃은 의도적으로 한쪽으로 몰려 있다. 다만 비대칭에도 규칙이 있어야 한다 — 아무 데나 놓는 것은 비대칭이 아니라 사고다.

  • 텍스트 블록을 그리드의 2/3 지점에서 끊고 나머지를 비워두기
  • 이미지를 화면 밖으로 흘려보내기(full 을 넘어 margin-inline: calc(var(--space-4) * -1))
  • 제목과 본문의 시작선을 일부러 어긋내기 — 단, 같은 어긋남을 페이지 전체에서 반복할 때만

벤토 그리드 주의

벤토(크기가 다른 카드들의 격자)는 더 이상 차별화가 아니라 새 기본값이다. 효과는 있지만(스크롤 깊이 증가) 표준화가 끝나서 "고민 안 했음"의 신호로 읽힌다. 쓰려면 최소한 다음 중 하나는 해라:

  • 카드 경계를 없애고 여백만으로 구획
  • 그리드에서 한 칸을 의도적으로 비우기
  • 카드 하나만 규칙을 깨고 밖으로 나가기

2. 시선 흐름

사람은 페이지를 읽지 않고 훑는다. 훑는 경로를 설계하는 것이 레이아웃의 본체다.

  1. 첫 화면에서 세 가지만 — 무엇인지 / 왜 좋은지 / 다음 행동. 넷 이상이면 아무것도 안 읽힌다
  2. 위계는 크기가 아니라 대비로 만든다 — 크기·굵기·색·여백 중 둘 이상을 동시에 바꿔야 위계가 보인다. 크기만 조금 키우는 것은 위계가 아니다
  3. 시선을 한 번은 멈춰라 — 전부 같은 리듬으로 흐르면 아무것도 강조되지 않는다. 섹션 하나는 리듬을 깨야 한다
  4. 스크롤은 보상이어야 한다 — 다음 화면에 무언가 새로운 것이 있어야 한다. 같은 카드 그리드가 세 번 반복되면 거기서 이탈한다

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 로 해결되는 것을 미디어쿼리로 만들지 마라. 미디어쿼리는 레이아웃 구조 자체가 바뀔 때만 쓴다.

모바일에서 먼저 확인해라

데스크톱에서 아름다운 것이 모바일에서 무너지는 것이 기본이고, 그 반대는 드물다. 특히:

  • 큰 타이포는 모바일에서 반드시 줄여라 (clamp 의 최소값)
  • 가로 스크롤이 생기는 요소를 찾아라 (overflow-x: hidden 으로 덮지 말고 원인을 고쳐라)
  • 터치 타깃 44×44px 이상
  • 호버로만 접근되는 기능을 만들지 마라

4-b. 모바일 내비 — 햄버거를 기본값으로 삼지 마라

내비 항목 수는 0단계에서 이미 정해져 있다(= 섹션 수). 구현하다 발견하면 늦다.

항목 수 처리 비용
2~4개 한 줄 유지. 자간을 줄이고 space-between 으로 편다 0
5~6개 가로 스크롤 줄, 또는 하단 고정 바 낮음
7개 이상 여는 메뉴 <details> 로 JS 0바이트

넷인데 햄버거를 쓰면 탭 한 번을 공짜로 뺏는 것이다. 반대로 여섯을 한 줄에 욱여넣으면 접힌다 — 실측에서 링크가 두 줄(top 46/77)에 걸렸고, 워드마크까지 3줄이 됐다.

2~4개 — 쌓는 것이 접히는 것보다 낫다

워드마크와 내비를 한 줄에 두면 둘 다 접힌다. 좁은 화면에서는 위아래로 쌓아라.

@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개 이상 — <details> 면 JS 가 필요 없다

키보드 조작·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-textauto 1fr 2칸인데 세로 라벨을 가로로 눕히자 그 긴 문장이 auto 칸을 통째로 먹어 h1 칸이 짜부라졌고, 헤드라인이 390px 에서 6줄, 320px 에서 11줄이 됐다.

넓은 화면에서는 멀쩡했다. 구조 변경의 대가는 항상 좁은 화면에서 먼저 청구된다.

규칙: 그리드 구조를 바꿨으면 그 자리에서 320·390·768 을 다시 재라. 나중에 하면 원인이 어느 변경이었는지 못 찾는다.


5. 이 단계의 통과 조건

  • CSS/HTML만으로 페이지가 완성됐다. JS를 꺼도 읽힌다
  • 모든 값이 3단계 토큰에서 나온다. 하드코딩된 px/색이 없다
  • 첫 화면에 메시지가 셋 이하다
  • 본문에 --measure 가 적용됐다
  • 모바일 폭 320px(iPhone SE 세로)에서 가로 스크롤이 없다
  • 키보드 Tab 만으로 모든 인터랙티브 요소에 도달한다. 포커스 링이 보인다
  • 한글이 있다면 조판 규칙이 적용됐다

근거: research/references/02-methodology.md, 03-trends-2026.md, 04-ai-slop-signatures.md (조사일 2026-08-20)