From b92853bf2b50c2719c76046d4716d9fed79bef33 Mon Sep 17 00:00:00 2001 From: Yun Chan Date: Fri, 21 Aug 2026 04:13:03 +0900 Subject: [PATCH] =?UTF-8?q?feat(skill):=20=EC=83=89=C2=B7=EB=AA=A8?= =?UTF-8?q?=EB=B0=94=EC=9D=BC=20=EB=82=B4=EB=B9=84=EB=A5=BC=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=EB=B7=B0=EC=97=90=20=EB=84=A3=EA=B3=A0=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=EC=A1=B0=EB=8B=AC=EC=9D=84=20=EB=8B=A8?= =?UTF-8?q?=EA=B3=84=EB=A1=9C=20=EC=8A=B9=EA=B2=A9=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 실제로 한 페이지를 만들어 보고 드러난 것들이다. 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//exec-.png 로 저장한다. 공식 래퍼는 "image_gen 이 호출되지 않았다"며 실패하지만 파일은 멀쩡히 있다. 실측에서 이 메시지를 보고 두 번 다시 만들었고 두 번 다 이미 있었다. 싣기 전 최적화도 넣었다(실측 10.1MB → 603KB). 구현에서 넘어진 것 셋. - images.md: 를 적으면 그 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줄이 됐다. --- packages/cli/package.json | 2 +- packages/core/package.json | 2 +- packages/skill/SKILL.md | 50 ++++++-- packages/skill/package.json | 2 +- packages/skill/references/antipatterns.md | 26 +++- packages/skill/references/images.md | 142 ++++++++++++++++++++++ packages/skill/references/layout.md | 73 +++++++++++ packages/skill/references/typography.md | 23 ++++ 8 files changed, 309 insertions(+), 11 deletions(-) create mode 100644 packages/skill/references/images.md diff --git a/packages/cli/package.json b/packages/cli/package.json index 25ae466..f3a46d0 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "designpaca", - "version": "0.3.0", + "version": "0.4.0", "description": "웹 디자인 파이프라인 스킬 — Claude Code · Codex · Cursor 에 한 줄로 설치한다", "keywords": [ "design", diff --git a/packages/core/package.json b/packages/core/package.json index 42263a0..2ecc665 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@designpaca/core", - "version": "0.3.0", + "version": "0.4.0", "private": true, "description": "designpaca 설치 엔진 — 타깃 어댑터, 매니페스트, 드리프트 감지", "type": "module", diff --git a/packages/skill/SKILL.md b/packages/skill/SKILL.md index 6d0de95..65dc7ce 100644 --- a/packages/skill/SKILL.md +++ b/packages/skill/SKILL.md @@ -107,14 +107,43 @@ description: "웹 디자인 전 과정을 끌고 가는 파이프라인 스킬. 결과를 크게 바꾸는 축을 골라 **선택지와 함께** 한 화면에 낸다. 각 선택지에는 "이걸 고르면 무엇이 달라지는지"를 적어라. 고르는 사람이 결과를 예상할 수 있어야 한다. -거의 모든 브리프에서 다음 넷이 결과를 가장 크게 바꾼다. +거의 모든 브리프에서 다음이 결과를 가장 크게 바꾼다. **위 넷은 거의 항상 묻는다.** -| 축 | 무엇이 달라지는가 | -|---|---| -| **업종·성격** | 정보 구조 전체. 같은 "꽃집"도 구독형과 하이엔드 스튜디오는 다른 사이트다 | -| **목표 행동** | CTA 의 수와 위치, 어떤 섹션이 필요하고 어떤 게 군더더기인지 | -| **톤** | 2단계 프리셋과 감수할 리스크가 여기서 결정된다 | -| **고유명사·실제 값** | 이름·지역·가격·연락처. 지어내면 하드 게이트 11에 걸린다 | +| 축 | 무엇이 달라지는가 | 안 물으면 | +|---|---|---| +| **업종·성격** | 정보 구조 전체. 같은 "꽃집"도 구독형과 하이엔드 스튜디오는 다른 사이트다 | 카테고리 평균이 나온다 | +| **목표 행동** | CTA 의 수와 위치, 어떤 섹션이 필요하고 어떤 게 군더더기인지 | CTA 가 넷이 되고 페이지가 무너진다 | +| **톤** | 2단계 프리셋과 감수할 리스크가 여기서 결정된다 | 내 기본 미학이 나온다 | +| **고유명사·실제 값** | 이름·지역·가격·연락처 | 지어내면 하드 게이트 11 | +| **기존 브랜드 색·로고** | 있으면 3단계 팔레트가 **거기서 시작한다** | 있는 자산을 무시하고 새로 만든다 | +| **좁은 화면의 내비** | 항목 5개 이상이면 구조가 달라진다 | 넓은 화면 메뉴를 그대로 접어 두 줄이 된다 | + +### 색은 반드시 확인한다 — 없다는 답도 답이다 + +**"브랜드 색이 있나요"** 는 결과를 되돌릴 수 없게 바꾸는 질문이다. +이미 로고·간판·패키지에 색이 있는데 사이트만 다른 색이면, 잘 만들어도 **틀린 것**이다. + +- **있다** → 3단계에서 그 색이 강조색이고, 레퍼런스는 그 색과 어울리는 곳으로 고른다 +- **없다·상관없다** → 레퍼런스 실측에서 뽑는다. 이때도 **어떤 색을 피해야 하는지**는 물어라 + (경쟁사가 쓰는 색, 대표가 싫어하는 색은 사용자만 안다) + +> 실측 사례: 꽃집 작업에서 색을 묻지 않고 레퍼런스 세 곳의 배경 평균(`#F8F6F0`)으로 정했다. +> 결과는 좋았지만 **운이 좋았던 것**이다. 브랜드 색이 있었다면 그걸 무시한 작업이 된다. + +### 좁은 화면의 내비를 미리 정한다 + +내비 항목 수는 0단계에서 이미 정해진다(= 섹션 수). **구현 단계에서 발견하면 늦다.** + +| 항목 수 | 좁은 화면 처리 | 비용 | +|---|---|---| +| 2~4개 | 그대로 한 줄. 자간을 줄이고 `justify-content: space-between` | 0 | +| 5~6개 | 가로 스크롤 줄 또는 하단 고정 바 | 낮음 | +| 7개 이상 | **여는 메뉴**(햄버거) | `
` 로 JS 0바이트 | + +**햄버거를 기본값으로 삼지 마라.** 항목이 넷인데 햄버거를 쓰면 한 번의 탭을 공짜로 뺏는 것이다. +반대로 여섯 개를 한 줄에 욱여넣으면 접힌다 — 실측에서 링크가 두 줄(top 46/77)에 걸렸다. + +여는 메뉴가 필요하다고 판단되면 `references/layout.md` 의 **모바일 내비** 절을 본다. 넷을 `multiSelect` 로 낼지 단일 선택으로 낼지 구분해라 — **목표 행동은 대개 복수**고, 톤과 성격은 하나여야 한다. 둘 다 고르면 방향이 서지 않는다. @@ -210,6 +239,12 @@ headed 브라우저가 있으면 스크린샷과 실측값을 바로 받는다 순서가 있다. **레이아웃 → 재질 → 모션.** 거꾸로 가면 화려한데 읽을 수 없는 페이지가 나온다. +**4-0. 이미지 조달** → `references/images.md` +자리를 만들기 전에 **무엇을 실을지** 정한다. 나중에 채우면 비율이 안 맞아 레이아웃을 다시 짠다. +사용자가 준 사진이 있으면 무조건 그것이고, 없으면 **codex 가 설치돼 있는지 확인해 생성**한다. +생성했으면 `design.md` 에 생성물이라고 적는다 — 나중에 실제 사진으로 바꿀 사람이 알아야 한다. +싣기 전에 반드시 줄인다(실측: 10.1MB → 603KB). + **4-1. 레이아웃과 타이포그래피** → `references/layout.md` (+ 폰트 적용은 `references/typography.md` §5, §6) 그리드, 여백 리듬, 시선 흐름. 3단계의 토큰을 그대로 쓴다. 이 단계가 끝나면 **아무 이펙트 없이도 완성된 페이지**여야 한다. 이것이 모든 폴백의 기반이다. @@ -307,6 +342,7 @@ HTML-in-Canvas(`drawElementImage`)는 **폴백을 완성한 뒤에만** 얹는 | `references/presets/README.md` | 2단계 — 미학 방향을 고를 때 (고른 프리셋 하나만 추가로 읽는다) | | `references/tokens.md` | 3단계 — 토큰을 정할 때 | | `references/typography.md` | 3단계 — 폰트를 고를 때 / 4-1 — 적용할 때 | +| `references/images.md` | **4-0 — 사진을 구하고 최적화할 때** | | `references/layout.md` | 4-1 — 그리드와 타이포 | | `references/svg-filters.md` | 4-2 — 재질을 만들 때 | | `references/three.md` | 4-3 — 입체가 필요할 때 | diff --git a/packages/skill/package.json b/packages/skill/package.json index cd4caa0..7722b4b 100644 --- a/packages/skill/package.json +++ b/packages/skill/package.json @@ -1,6 +1,6 @@ { "name": "@designpaca/skill", - "version": "0.3.0", + "version": "0.4.0", "private": true, "description": "designpaca 스킬 원본 — SKILL.md 와 참조 문서", "scripts": { diff --git a/packages/skill/references/antipatterns.md b/packages/skill/references/antipatterns.md index 27a7e26..ed1bfab 100644 --- a/packages/skill/references/antipatterns.md +++ b/packages/skill/references/antipatterns.md @@ -328,7 +328,29 @@ const s = await sharp(buf).stats(); // 잘라낸 버퍼를 다시 물려야 > 지나가는 프레임은 방문자도 놓치고 측정도 매번 다른 값을 준다. > 정지된 before/after 두 장이 더 정확하게 말한다. -**④ 눈으로 의심한 것이 실측에서 뒤집힐 수 있다.** +**④ 개별 요소의 높이로는 "여러 줄에 걸친 것" 을 못 잡는다.** +"컨트롤이 2줄로 접히는가"(하드 게이트 6)를 이렇게 쟀다. + +```js +// 틀렸다 — 이 검사는 "접힘 0" 이라고 답한다 +[...document.querySelectorAll('.nav a')] + .filter(e => e.getBoundingClientRect().height > lineHeight * 1.8) +``` + +각 링크는 한 줄짜리라 높이가 정상이었다. **줄바꿈된 것은 링크가 아니라 컨테이너였다** — +링크들이 두 행(top 46 / 77)에 나뉘어 놓여 있었다. 게이트를 통과했다고 보고했고, +사용자가 화면을 보고 잡아냈다. + +```js +// 맞다 — 행이 몇 개인지는 top 값의 종류로 센다 +new Set([...document.querySelectorAll('.nav a')] + .map(e => Math.round(e.getBoundingClientRect().top))).size +``` + +같은 함정이 갤러리·태그 목록·버튼 그룹 어디에나 있다. +**"몇 줄인가" 를 물을 때는 높이가 아니라 위치를 세라.** + +**⑤ 눈으로 의심한 것이 실측에서 뒤집힐 수 있다.** WebGL 판이 본문 뒤를 지나가는 화면을 보고 "대비가 죽었다" 고 판단해 캔버스를 어둡게 만들려 했다. 스크린샷에서 글자 사이 빈 띠의 배경 휘도를 재보니 최악 지점이 **9.83:1** — AAA(7:1)를 넘었다. 고칠 필요가 없었다. @@ -442,6 +464,8 @@ sticky 캔버스가 끝까지 돌려면 목록 뒤에 스크롤 구간이 필요 - [ ] (한국어) 한글 폰트가 명시되어 있고 `word-break: keep-all`이 있다 - [ ] 모든 섹션에서 제목→본문 간격이 **같은 값**이다 (0px 인 섹션이 하나도 없다) - [ ] 화면에 적은 수치는 **전부 그 화면을 캡처해 잰 값**이다 (추정치·기대치가 섞여 있지 않다) +- [ ] 이미지 높이를 실제로 쟀다 — 원본 픽셀 높이가 그대로 나오면 `aspect-ratio` 가 죽은 것이다 +- [ ] 내비·버튼 그룹의 **행 수를 `top` 값으로** 셌다 (개별 높이로는 못 잡는다) **판정** diff --git a/packages/skill/references/images.md b/packages/skill/references/images.md new file mode 100644 index 0000000..41dad76 --- /dev/null +++ b/packages/skill/references/images.md @@ -0,0 +1,142 @@ +# images.md — 사진을 어디서 구하고 어떻게 싣나 + +이미지는 대부분의 디자인에서 **가장 무거운 자산이고 가장 큰 인상**이다. +레이아웃이 좋아도 사진이 약하면 페이지가 약하다. 반대도 마찬가지다. + +4단계(구현)에서 **레이아웃을 잡기 전에** 무엇을 실을지 정해라. +자리를 먼저 만들고 나중에 채우면 비율이 안 맞아 다시 짠다. + +--- + +## 1. 조달 경로 — 위에서부터 확인한다 + +| 순위 | 경로 | 언제 | +|---|---|---| +| 1 | **사용자가 준 사진** | 있으면 무조건 이것. 실제 작업물을 이길 생성물은 없다 | +| 2 | **생성**(codex `image_gen`) | 콘셉트 확인용, 톤이 정확히 지정된 추상·분위기 컷 | +| 3 | **스톡** | 라이선스가 명확한 곳만. 출처를 `design.md` 에 적는다 | +| 4 | 없이 간다 | 타이포·색·여백만으로 만든다. 약한 사진보다 낫다 | + +**0단계에서 물어라**: "쓸 수 있는 사진이 있나요?" +없다고 하면 2번으로 가되, **생성물이라는 사실을 `design.md` 에 적는다.** +나중에 실제 사진으로 바꿀 때 레이아웃을 다시 짜지 않도록 비율을 미리 고정해 둔다. + +--- + +## 2. codex 가 있으면 생성한다 + +설치 여부를 먼저 확인한다. 없으면 조용히 3번으로 내려간다. + +```bash +codex --version # 있나 +node -p "JSON.parse(require('fs').readFileSync(require('os').homedir()+'/.codex/auth.json','utf8')).auth_mode" +# → "chatgpt" 여야 구독 경로다. "api_key" 면 사용자 과금이라 먼저 물어라. +``` + +### 래퍼가 실패해도 이미지는 만들어져 있다 — 반드시 확인해라 + +`~/.codex/imagegen-headless/codex_imagegen.sh` 는 세션 rollout 에서 +`image_generation_call` 의 **base64** 를 찾는다. **codex-cli 0.147.0 은 base64 를 돌려주지 않는다.** +`exec` 도구를 거쳐 **파일로 저장**하고, 래퍼는 "image_gen 이 호출되지 않았다" 며 실패한다. + +실측에서 이 메시지를 보고 두 번 다시 만들었는데, **두 번 다 파일은 멀쩡히 있었다.** + +``` +~/.codex/generated_images//exec-.png +``` + +rollout jsonl 의 `custom_tool_call_output` 에 경로가 그대로 적혀 있다. + +``` +Generated images are saved to <디렉터리> as <디렉터리>\exec-.png by default. +``` + +**한 줄에 경로가 둘**이므로 마지막 것을 쓴다. 공백을 허용하는 정규식은 +앞 디렉터리부터 `" as "` 까지 통째로 삼킨다. + +```js +// 세션 파일에서 실제 저장 경로를 읽는다 +const all = line.match(/[A-Za-z]:\\[^\s"]*generated_images[^\s"]*\.png/g); +if (all) hit = all[all.length - 1]; +``` + +호출은 이렇게 한다. **`$imagegen` 이 프롬프트 맨 앞에 와야** 내장 도구를 쓴다 — +뒤에 붙이면 codex 가 스크립트를 짜려고 헤맨다. + +```bash +printf '%s' "\$imagegen $PROMPT" \ + | codex exec --skip-git-repo-check --dangerously-bypass-approvals-and-sandbox - +``` + +> 실패로 판정하기 전에 `~/.codex/generated_images/` 를 봐라. +> "실패했다" 고 보고했는데 파일이 있으면 그건 도구가 아니라 판정이 틀린 것이다. + +### 프롬프트 — 톤을 공통 문자열로 고정한다 + +여러 장을 만들 때 각 프롬프트에 톤을 다시 쓰면 장마다 색이 달라진다. +**공통 접두사 하나**를 만들고 장면만 바꿔라. + +``` +[공통] Editorial magazine photograph. Warm off-white setting (#F8F6F0), + soft natural window light, low saturation, muted restrained palette. + Photographic realism, shallow depth of field, fine film grain. + No text, no logo, no watermark. +[개별] Overhead flat-lay on a worn wooden workbench: cut stems, florist shears, twine. +``` + +- **3단계에서 정한 배경색을 프롬프트에 그대로 넣어라.** 사진이 페이지 배경과 이어진다 +- 크기는 양변 16의 배수: `1152x1536`(3:4) · `1536x1152`(4:3) · `2048x1152`(16:9) +- 사람 얼굴은 피하는 쪽이 안전하다. 손·뒷모습·부분은 잘 나오고 얼굴은 어색해지기 쉽다 +- `No text` 를 넣어라. 넣지 않으면 간판·라벨에 뭉개진 글자가 생긴다 + +### 생성물은 반드시 눈으로 본다 + +`Read` 로 열어 확인한 뒤에만 쓴다. 특히 **공간 사진**(작업실·매장)이 어색해지기 쉽다. +주제가 안 맞으면 프롬프트를 **한 번에 한 가지만** 바꿔 다시 만든다. + +--- + +## 3. 싣기 전에 반드시 줄인다 + +생성물은 1~3MB PNG 로 나온다. 그대로 실으면 페이지가 10MB 가 된다. + +```js +const w = Math.min(meta.width, meta.width > meta.height ? 1600 : 1100); +await sharp(src).resize({ width: w }).webp({ quality: 82 }).toFile(out); +``` + +실측: **10.1MB → 603KB (94% 감소).** 화면에서 차이가 보이지 않는다. + +| 쓰임 | 최대 폭 | +|---|---| +| 전체폭 배경·히어로 | 1600px | +| 본문 폭 이미지 | 1100px | +| 카드·썸네일 | 640px | + +## 4. `` 에 `width`/`height` 를 적었다면 `height: auto` 를 반드시 줘라 + +CLS 를 막으려고 속성을 적는 것은 맞다. 그런데 CSS 에서 높이를 정하지 않으면 +**속성 height 가 그대로 계산된 높이가 되어 `aspect-ratio` 가 무시된다.** + +```css +img { max-width: 100%; height: auto; } /* 이 한 줄이 없으면 */ +``` + +실측 사고: `.two-photo img { aspect-ratio: 3/4 }` 를 줬는데 computed 값은 +`"3 / 4"` 로 멀쩡히 보이고 **실제 높이는 원본 1448px** 이었다. +`aspect-ratio` 는 폭·높이 중 하나가 `auto` 일 때만 높이를 정한다. + +증상이 조용하다 — 오류도 경고도 없고 **페이지만 길어진다.** +이 한 줄을 넣자 전체 높이가 10.9 화면에서 7 화면이 됐다. + +> 진단: 이미지 높이를 실제로 재라. 원본 픽셀 높이(예: 1448)가 그대로 나오면 이것이다. +> ```js +> [...document.querySelectorAll('img')] +> .filter(i => i.getBoundingClientRect().height > 1000) +> ``` + +## 5. `alt` 는 장식이 아니면 반드시 쓴다 + +- 내용을 나르는 이미지 → **무엇이 보이는지** 적는다. "이미지", "사진" 은 alt 가 아니다 +- 순수 장식(배경 텍스처·그레인) → `alt=""` + `aria-hidden="true"` +- 캡션이 이미 설명하고 있으면 alt 는 짧게. 같은 문장을 두 번 읽히지 않는다 diff --git a/packages/skill/references/layout.md b/packages/skill/references/layout.md index 379ff7f..42b821c 100644 --- a/packages/skill/references/layout.md +++ b/packages/skill/references/layout.md @@ -105,6 +105,79 @@ --- +## 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를 꺼도 읽힌다 diff --git a/packages/skill/references/typography.md b/packages/skill/references/typography.md index fe31702..dcc71c3 100644 --- a/packages/skill/references/typography.md +++ b/packages/skill/references/typography.md @@ -270,6 +270,29 @@ font-family: "Switzer", "Pretendard Variable", system-ui, sans-serif; --- +### 세로쓰기에 가로쓰기 자간을 그대로 쓰지 마라 + +`writing-mode: vertical-rl` 로 세운 글자에 라벨용 자간(`letter-spacing: 0.14em`)을 +그대로 물리면 **글자 사이 세로 간격**이 되어 낱글자가 흩어진다. + +한글은 자소가 모여 한 글자를 이루기 때문에 라틴보다 심하다 — +"플로럴 스튜디오" 가 낱글자 일곱 개로 읽히기 시작한다. + +```css +.vertical { + writing-mode: vertical-rl; + letter-spacing: 0.02em; /* 가로쓰기 라벨의 0.14em 을 그대로 쓰면 안 된다 */ +} +``` + +세로쓰기는 **자간이 아니라 `line-height` 로 숨을 만든다.** 세로쓰기에서 `line-height` 는 +글자 사이가 아니라 **줄(세로 기둥) 사이**를 벌린다. + +> 좁은 화면에서는 세로 글자가 자리를 못 얻는다. `writing-mode: horizontal-tb` 로 눕히되, +> **그 자리의 그리드 칼럼도 같이 손봐라** — 가로로 누운 긴 문장이 `auto` 칸을 통째로 먹는다. + +--- + ## 7. 라이선스 **배포 전에 확인한다.** 폰트 라이선스 위반은 조용히 있다가 청구서로 온다.