diff --git a/apps/site/public/work/font-foundry/design.md b/apps/site/public/work/font-foundry/design.md new file mode 100644 index 0000000..0ca942d --- /dev/null +++ b/apps/site/public/work/font-foundry/design.md @@ -0,0 +1,47 @@ +# design.md — 한성활자연구소 + +## 브리프 + +- **무엇을**: 가상 서체 기획을 정직하게 밝히면서, 실제 마루 부리와 Pretendard로 문장 조판의 차이를 즉시 체험하는 한국어 타입 스튜디오. +- **누구에게**: 활자를 고르거나 본문 조판 값을 결정해야 하는 편집자·디자이너·개발자. +- **제약**: ko-KR 한국어, 밝은 아이보리·검정 본문·버밀리언 정체성 유지. 가상 서체를 판매하거나 가짜 스펙을 제시하지 않으며, 본문은 최대 42rem. 320px부터 가로 넘침이 없어야 한다. + +## 리디자인 감사와 레퍼런스 + +이전 판은 한 글자 표본과 실제 렌더 서체의 관계가 멀고, 11,172자·1000 UPM 같은 가상 수치가 체험보다 앞섰다. 실제 사용자가 판단할 수 있는 것은 문장 안의 폭·획·간격이므로 그 역할을 첫 화면으로 올렸다. + +| 출처 ID · 슬롯 | 출처 | 관찰 | 채택한 원리와 검증 | +| --- | --- | --- | --- | +| `KLIM-FONTS` · R1 구조 | https://klim.co.nz/fonts/ | Display, Text, Narrow, Condensed처럼 같은 계열의 역할과 폭을 나란히 제시한다. | 글꼴을 사양 표가 아니라 읽는 역할로 비교한다. 같은 문장을 두 역할로 렌더해 비교한다. | +| `CHIPPERFIELD-PROJECTS` · R2 톤 | https://davidchipperfield.com/projects | 프로젝트명·연도·필터가 정렬선과 짧은 메타 안에서 읽힌다. | 큰 표본 아래에 기준선, 짧은 메타, 한 가지 행동을 정렬한다. 1440px 첫 화면에서 표본·상태·CTA의 순서를 확인한다. | +| `SANDOLL-TYPELAB` · R3 디테일 | https://typelab.sandoll.co.kr/experience | 권장규격과 맞춤규격, 한글 범위를 선택하며 표본을 실제 작업으로 연결한다. | 버튼과 조판 슬라이더가 즉시 보이는 결과를 바꾸게 한다. 키보드·range·리셋으로 값을 바꾸어 검증한다. | +| `WCAG-READ` · 접근성 | 200% 텍스트 확대와 320 CSS px reflow를 핵심 화면에서 별도로 시험한다. | 큰 활자 장면은 접근성 제목을 대신하지 않는다. | 320px 200% 텍스트에서 문서 폭 320px, h1 80px을 기록하고 장식 표본은 `aria-hidden`으로 둔다. | + +## 방향 + +- **컨셉**: 문장이 보여 주는 활자의 차이. +- **프리셋**: `editorial` — 읽는 시간이 주제이므로 따뜻한 바탕, 제한된 색, 좁은 본문 폭, 세리프 표제를 쓴다. +- **감수한 리스크**: 이미지를 히어로에서 빼고 버밀리언 세 글자 표본 하나를 가장 큰 장면으로 사용한다. 제목과 설명은 별도 DOM으로 남겨 접근성 판단을 표본에 기대지 않는다. + +## 토큰과 구현 + +- **폰트**: 마루 부리는 제목·조판 시험대·표본의 제목 상태, Pretendard는 본문·기본 표본 상태. 두 폰트 모두 CDN 웹폰트이며 `font-synthesis-weight: none`으로 합성 굵기를 막는다. +- **색**: `--surface #f5f1e8`, `--ink #26221d`, `--accent #a63d2f`. 버밀리언은 표본과 행동에만 쓴다. +- **크기**: h1 `clamp(40px, 1.3rem + 4.2vw, 80px)`, 표본 `clamp(96px, 17.5vw, 260px)`, 본문 폭 최대 `42rem`. +- **레이아웃**: 데스크톱 표본 레일 최대 1700px, 일반 콘텐츠 1216px. 모바일은 최소 20px 인셋을 지키고 표본은 한 줄에 남는다. +- **상호작용**: native 버튼 두 개의 `aria-pressed`가 표본 폰트와 상태 문구를 바꾼다. 기존의 크기·자간·행간 range와 리셋은 유지한다. +- **성능**: 새 JavaScript 라이브러리·이미지·애니메이션 없음. 초기 체험은 인라인 DOM 코드만 사용한다. 활자 케이스의 두 장면은 생성 이미지임을 캡션으로 밝힌다. + +## 의도적으로 하지 않은 것 + +- 마루 부리·Pretendard를 가상의 한성 서체로 속여 판매하지 않는다. 가상 기획과 실제 구현 폰트를 표본 바로 위와 푸터에 밝힌다. +- 인장·붓글씨·세로쓰기·종이결처럼 한국 활자를 이국화하는 장식을 사용하지 않는다. 활자의 형태와 조판만으로 분위기를 만든다. +- 움직임으로 활자의 차이를 설명하지 않는다. reduced motion에서도 같은 표본과 모든 조작이 남는다. + +## 검증 기록 · 2026-09-12 + +- `outputs/design-expertise-20260912/font/font-redesign-e2e.mjs`가 독립 정적 서버에서 320·390·768·1440·1920·2560px를 열었다. 각 폭의 문서 `scrollWidth`는 뷰포트 폭과 같고, 내비는 한 줄, 버튼은 최소 44px이었다. +- 320px에서 200% 텍스트 확대 후 문서 폭은 320px, 서술형 h1은 80px으로 실제 확대됐다. 장식 표본은 `aria-hidden="true"`로 남았다. +- `prefers-reduced-motion: reduce`에서 `scroll-behavior`는 `auto`였다. 키보드 Space로 제목 버튼을 선택하면 `aria-pressed`와 상태 문구가 마루 부리로 바뀌었다. +- 세 range는 24px·0.05em·2.2를 표본과 캡션에 적용했고, 리셋은 17px·0em·1.8을 둘 다 복원했다. +- 지연 이미지 두 장은 scrollIntoView 뒤 `decode()`를 통과했고, `metaltype.webp` 응답은 HTTP 200·naturalWidth 900이었다. `font-foundry-1440-first.png`, `font-foundry-390-first.png`, 전체 캡처와 JSON 보고서는 같은 outputs 폴더에 있다. diff --git a/apps/site/public/work/font-foundry/index.html b/apps/site/public/work/font-foundry/index.html index 8eb884f..b775893 100644 --- a/apps/site/public/work/font-foundry/index.html +++ b/apps/site/public/work/font-foundry/index.html @@ -3,287 +3,165 @@ - 한성활자연구소 · 본문용 한글 활자 - - - + 한성활자연구소 · 읽는 리듬을 만드는 활자 + + + - - - - - + + + + - -
한성활자연구소
-
-
-
-

본문용 한글 활자 · 1000 UPM · 2018년 창립

-

한

-
-

- 이 글자가 어디에 쓰이는지는 이 페이지 전체가 증명합니다. 우리는 - 제목용 활자를 만들지 않습니다 — 오래 읽어야 하는 본문의 활자만 - 만듭니다. 획의 장력, 자간의 균형, 행간의 숨을 그 단위(UPM)로 - 측정합니다. -

-
-
-
서체
-
한성본문 · 한성제목
-
-
-
글자 수
-
11,172자 완성
-
-
-
단위
-
1000 UPM · 가변 웨이트 3종
-
-
+
+
+
+

한글 타입 스튜디오

+

읽는 리듬을
만드는 활자.

+
+
+

글자는 모양만으로 읽히지 않습니다. 문장 안에서 어떤 폭으로 서고, 얼마나 가까이 호흡하는지가 읽는 시간을 바꿉니다.

+ 조판해 보기 +
-
-
- - -
-
-

활자 두 종

- -
-
-

한성본문

-

본문용 · 웨이트 400 · 16px에서 행간 1.8

-
-

- 아침 안개가 걷히기 전에 도랑물 소리가 먼저 들렸다. 글자는 종이 위에서 - 소리를 내지 않지만, 잘 짜인 활자는 소리 내어 읽게 만든다. 획이 닿는 - 각도와 글자가 서로 미는 힘을 천 번쯤 고쳐 썼다. 그 결과 이 문장이 - 밤늦게도 읽히는 것이다. -

-
- -
-
-

한성제목

-

제목·표제용 · 웨이트 600 · 큰 짝에서 벌어짐 0

-
-

- 활자는 읽는 리듬을 설계한다 -

-
-
-
- - -
-
-

조판 시험대

-

- 자간과 행간, 글자 크기가 가독성을 어떻게 바꾸는지 직접 움직여 보세요. - 아래 값은 권장값이 아니라 출발점입니다 — 용지 규격과 본문 길이에 따라 - 정답은 달라집니다. -

- -
-
-
-
17
- +
+

가상 서체 기획 · 화면은 마루 부리와 Pretendard로 구현

+ + + +
+
+
+
+
+

두 가지 읽기 방식

+

같은 문장이
다른 속도로 읽힌다.

+
+
+
+
+

본문

+

Pretendard · 작고 긴 문장에 필요한 명료한 골격

+
+

아침 안개가 걷히기 전에 도랑물 소리가 먼저 들렸다. 글자는 종이 위에서 소리를 내지 않지만, 잘 짜인 활자는 소리 내어 읽게 만든다.

+
+
+
+

제목

+

마루 부리 · 표제에서 멈춤과 여백을 만드는 결

+
+

아침 안개가 걷히기 전에 도랑물 소리가 먼저 들렸다. 글자는 종이 위에서 소리를 내지 않지만, 잘 짜인 활자는 소리 내어 읽게 만든다.

+
+
+
+
+
+
+
+
+

조판 시험대

+

읽는 호흡을
직접 맞춰 보세요.

+
+

크기는 문장의 밀도를, 자간은 낱글자 사이의 공기를, 행간은 다음 줄로 넘어가는 속도를 바꿉니다. 하나씩 움직여 같은 문장이 어떻게 달라지는지 비교해 보세요.

+
+
+
+
17px
+
0em
+
1.8
+ +
+
+

마루 부리 · 17px · 자간 0em · 행간 1.8

+
활자의 역사는 글자를 찍는 기계의 역사가 아니라, 글자를 읽는 눈의 역사였다. 활자소의 식자공들은 하루에 만 글자를 짜면서 어느 글꼴이 눈을 덜 아프게 하는지 알고 있었다. 그 지식은 문서로 남지 않았고 손끝으로만 전해졌다. 이 미리보기는 그 판단을 눈으로 비교할 수 있게 옮긴 것입니다. 자간을 0에서 5로 바꾸면 문단의 호흡과 줄 길이가 어떻게 달라지는지 확인할 수 있습니다.
- -
-
- 나무 활자 케이스에 골라 담긴 금속 활자들 — 활자면이 위에서 빛을 받아 결이 보인다 -
연구소 2층 · 금속 활자 케이스
+
+ 나무 활자 케이스에 골라 담긴 금속 활자들 — 활자면이 위에서 빛을 받아 결이 보인다 +
생성 이미지 · 금속 활자 케이스의 관찰 장면
-

금속에서 시작해
화면으로 끝난다

-

- 한성본문의 자간 규칙은 금속 활자의 '옆면 여백'에서 왔습니다. 납활자는 - 글자 바로 옆에 살이 붙어 있어서, 찍히는 글자 사이는 언제나 일정했고 - 그 일정함이 읽는 리듬이었습니다. 화면용 폰트는 그 살을 지울 수 - 있지만, 우리는 지우지 않았습니다. -

-
- 빛을 받은 금속 활자와 정렬된 활자 케이스 -
활자 폭과 여백을 비교하는 편집 자리
+

활자 케이스

+

금속에서 시작해
화면에서 읽힌다.

+

이 가상 기획은 금속 활자의 옆면 여백을 관찰의 출발점으로 삼습니다. 납활자는 글자 바로 옆에 살이 붙어 있어, 찍히는 글자 사이가 일정했습니다. 이 화면에서는 그 일정함이 문장 안의 자간과 행간에 어떤 리듬을 만드는지 살펴봅니다.

+
+ 빛을 받은 금속 활자와 정렬된 활자 케이스 +
생성 이미지 · 활자 폭과 여백을 보는 편집 장면
- - -
+
-

구입

-

- 웹·출판·임베드 세 가지 라이선스만 있습니다. 사용 수를 세지 않습니다 — - 조판되는 단위(도서 권수, 사이트 도메인, 기기)로 셉니다. -

+

가상 서체 기획

+

라이선스 안내

+

아래 라이선스 종류와 가격은 서체 판매 정보를 구성하는 방식을 보여 주는 예시입니다. 실제 주문·결제·문의로 이어지지 않습니다.

-
-
-

웹

-

도메인당 · 웨이트 전종 · 자체 호스팅 WOFF2

-
-

140,000원

-
-
-
-

출판

-

도서 권수당 · 인쇄+전자 동시

-
-

220,000원

-
+

웹

도메인당 · 웨이트 전종 · 자체 호스팅 WOFF2

예시 140,000원

+

출판

도서 권수당 · 인쇄+전자 동시

예시 220,000원

- 견적 문의 + 조판해 보기
- - - + diff --git a/apps/site/public/work/font-foundry/styles/main.css b/apps/site/public/work/font-foundry/styles/main.css index efa3ae7..efd97d1 100644 --- a/apps/site/public/work/font-foundry/styles/main.css +++ b/apps/site/public/work/font-foundry/styles/main.css @@ -1,413 +1,109 @@ -@import url("./tokens.css"); +@import url("./tokens.css?v=3"); -/* ========================================================================== - 한성활자연구소 — editorial-archival. 글자가 상품. - ========================================================================== */ +*, *::before, *::after { box-sizing: border-box; } +* { margin: 0; } +html { background: var(--surface); scroll-behavior: smooth; -webkit-text-size-adjust: 100%; } +body { background: var(--surface); color: var(--ink); font-family: var(--font-body); font-size: var(--step-0); line-height: var(--leading-normal); font-synthesis-weight: none; -webkit-font-smoothing: antialiased; } +:where(h1, h2, h3) { font-family: var(--font-display); font-weight: 600; line-height: var(--leading-tight); text-wrap: balance; word-break: keep-all; overflow-wrap: break-word; } +h2 { font-size: var(--step-3); } +h3 { font-family: var(--font-body); font-size: var(--step-1); font-weight: 600; } +:where(p, li, dd, dt, th, td, figcaption) { overflow-wrap: break-word; word-break: keep-all; text-wrap: pretty; } +a { color: inherit; } +.muted { color: var(--ink-muted); } +.eyebrow, .stage-label, .bench-caption { color: var(--ink-muted); font-size: var(--step--1); letter-spacing: 0.04em; } +:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; } +.skip { position: absolute; z-index: 100; top: 0; left: -9999px; padding: var(--space-3) var(--space-4); background: var(--ink); color: var(--surface); } +.skip:focus { left: 0; } +.wrap { width: min(100% - 2 * var(--pad-inline), var(--container)); margin-inline: auto; } -*, -*::before, -*::after { - box-sizing: border-box; -} -* { - margin: 0; -} +.site-head { position: sticky; z-index: 10; top: 0; border-bottom: 1px solid var(--line); background: color-mix(in srgb, var(--surface) 94%, transparent); backdrop-filter: blur(6px); } +.head-inner { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); min-block-size: 4.25rem; } +.wordmark { font-family: var(--font-display); font-size: var(--step-1); font-weight: 600; text-decoration: none; } +.nav { display: flex; gap: clamp(var(--space-3), 2vw, var(--space-5)); font-size: var(--step--1); } +.nav a { padding-block: var(--space-2); color: var(--ink-muted); text-decoration: none; } +.nav a:hover { color: var(--accent); } -html { - -webkit-text-size-adjust: 100%; - scroll-behavior: smooth; - background: var(--surface); -} -@media (prefers-reduced-motion: reduce) { - html { - scroll-behavior: auto; - } -} +.hero { padding-block: clamp(var(--space-6), 7vw, var(--space-8)) var(--space-7); } +.hero-rail { width: min(100% - 2 * var(--pad-wide), var(--container-wide)); margin-inline: auto; } +.hero-intro { display: grid; grid-template-columns: minmax(0, 7fr) minmax(17rem, 3fr); gap: var(--space-6) clamp(var(--space-5), 7vw, var(--space-8)); align-items: end; } +.hero h1 { margin-top: var(--space-3); font-size: clamp(2.5rem, 1.3rem + 4.2vw, 5rem); max-inline-size: 10ch; } +.hero-note { max-inline-size: 27rem; padding-bottom: var(--space-2); } +.text-link { display: inline-flex; gap: var(--space-2); margin-top: var(--space-4); color: var(--accent); font-weight: 600; text-decoration-thickness: 1px; text-underline-offset: 0.25em; } +.specimen-stage { margin-top: clamp(var(--space-6), 8vw, var(--space-8)); color: var(--accent); } +.stage-label { color: var(--ink-muted); } +.hero-specimen { min-inline-size: 0; margin-top: var(--space-4); font-family: var(--font-body); font-size: clamp(6rem, 17.5vw, 16.25rem); font-weight: 650; letter-spacing: -0.09em; line-height: 0.82; text-wrap: nowrap; } +.specimen-stage[data-kind="title"] .hero-specimen { font-family: var(--font-display); font-weight: 600; letter-spacing: -0.11em; } +.baseline { block-size: 1px; margin-top: var(--space-3); background: var(--accent); } +.stage-footer { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); padding-top: var(--space-3); } +.type-switch { display: flex; gap: var(--space-2); } +.type-choice, .btn-reset { min-block-size: 2.75rem; border: 1px solid var(--line); border-radius: var(--radius); background: transparent; color: var(--ink); cursor: pointer; font: inherit; padding-inline: 0.9rem; } +.type-choice[aria-pressed="true"] { border-color: var(--accent); background: var(--accent); color: var(--accent-ink); } +.type-status { color: var(--ink-muted); font-size: var(--step--1); text-align: end; } -body { - background: var(--surface); - color: var(--ink); - font-family: var(--font-body); - font-size: var(--step-0); - line-height: var(--leading-normal); - -webkit-font-smoothing: antialiased; -} +.section { padding-block: var(--space-8); } +.band { border-block: 1px solid var(--line); background: var(--surface-raised); } +.section-head { display: grid; grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); gap: var(--space-5); align-items: baseline; } +.comparison { margin-top: var(--space-7); border-top: 1px solid var(--line); } +.typeface { display: grid; grid-template-columns: minmax(10rem, 3fr) minmax(0, 7fr); gap: var(--space-5) var(--space-7); padding-block: var(--space-6); border-bottom: 1px solid var(--line); } +.typeface-head .muted { margin-top: var(--space-2); font-size: var(--step--1); } +.specimen { max-inline-size: 42rem; } +.specimen-body { font-family: var(--font-body); font-size: 1.15rem; line-height: 1.9; } +.specimen-display { font-family: var(--font-display); font-size: clamp(1.7rem, 1.2rem + 2.3vw, 3.25rem); line-height: 1.48; } -:where(h1, h2, h3) { - font-family: var(--font-display); - font-weight: 600; - line-height: var(--leading-tight); - word-break: keep-all; - overflow-wrap: break-word; - text-wrap: balance; -} -h2 { - font-size: var(--step-2); -} -h3 { - font-family: var(--font-body); - font-weight: 600; - font-size: var(--step-1); -} -:where(p, li, dd, dt, th, td, figcaption) { - word-break: keep-all; - overflow-wrap: break-word; - line-height: var(--leading-normal); - text-wrap: pretty; -} +.bench-heading { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 4fr); gap: var(--space-6); align-items: end; } +.bench-lead { max-inline-size: 28rem; } +.bench { display: grid; grid-template-columns: minmax(13rem, 3fr) minmax(0, 7fr); gap: var(--space-6) var(--space-7); margin-top: var(--space-6); padding-top: var(--space-5); border-top: 1px solid var(--line); } +.bench-controls { display: grid; align-content: start; gap: var(--space-4); } +.control { display: grid; gap: var(--space-2); } +.control-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); } +.control label, .control output { color: var(--ink-muted); font-size: var(--step--1); } +.control output, .order-price { font-variant-numeric: tabular-nums; } +input[type="range"] { inline-size: 100%; accent-color: var(--accent); } +.btn-reset { justify-self: start; margin-top: var(--space-2); } +.btn-reset:hover, .type-choice:hover { border-color: var(--accent); } +.bench-caption { margin-bottom: var(--space-3); } +.bench-sample { max-inline-size: 42rem; border: 1px solid var(--line); background: var(--surface); padding: clamp(var(--space-4), 3vw, var(--space-6)); font-family: var(--font-display); font-size: 1.0625rem; line-height: 1.8; } -.muted { - color: var(--ink-muted); -} -a { - color: inherit; -} - -:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 3px; -} - -.skip { - position: absolute; - left: -9999px; - top: 0; - background: var(--ink); - color: var(--surface); - padding: var(--space-3) var(--space-4); - z-index: 100; -} -.skip:focus { - left: 0; -} - -.wrap { - width: min(100% - 2 * var(--pad-inline), var(--container)); - margin-inline: auto; -} - -/* ── 헤더 ─────────────────────────────────────────────────────────────── */ - -.site-head { - position: sticky; - top: 0; - z-index: 50; - background: color-mix(in srgb, var(--surface) 90%, transparent); - backdrop-filter: blur(6px); - border-bottom: 1px solid var(--line); -} -.head-inner { - display: flex; - align-items: baseline; - justify-content: space-between; - gap: var(--space-4); - padding-block: var(--space-3); -} -.wordmark { - font-family: var(--font-display); - font-size: var(--step-1); - font-weight: 600; - text-decoration: none; -} -.nav { - display: flex; - gap: clamp(var(--space-3), 2vw, var(--space-5)); - font-size: var(--step--1); -} -.nav a { - text-decoration: none; - color: var(--ink-muted); - padding-block: 2px; - border-bottom: 1px solid transparent; - transition: color var(--dur-quick) var(--ease-soft), - border-color var(--dur-quick) var(--ease-soft); -} -.nav a:hover { - color: var(--ink); - border-color: var(--accent); -} - -/* ── 스페시멘 히어로 ──────────────────────────────────────────────────── */ - -.hero { - padding-block: var(--space-7) var(--space-8); -} -.hero-kicker { - font-size: var(--step--1); - color: var(--ink-muted); - letter-spacing: 0.06em; -} -.hero-glyph { - font-size: clamp(11rem, 24vw, 22rem); - line-height: 0.95; - margin-top: var(--space-4); - position: relative; -} -.hero-side { - margin-top: var(--space-5); - display: grid; - grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); - gap: var(--space-5) var(--space-7); - align-items: start; -} -.hero-statement { - max-width: 32em; -} -.hero-facts { - display: grid; - gap: var(--space-3); - border-left: 1px solid var(--line); - padding-left: var(--space-5); -} -.hero-facts dt { - font-size: var(--step--1); - color: var(--ink-faint); -} -.hero-facts dd { - margin-top: 2px; - font-weight: 500; -} - -/* ── 섹션 공통 ────────────────────────────────────────────────────────── */ - -.section { - padding-block: var(--space-8); -} -.band { - background: var(--surface-raised); - border-block: 1px solid var(--line); -} - -/* ── 활자 두 종 ───────────────────────────────────────────────────────── */ - -.typeface + .typeface { - margin-top: var(--space-6); -} -.typeface-head { - display: flex; - justify-content: space-between; - align-items: baseline; - gap: var(--space-4); - flex-wrap: wrap; - border-top: 1px solid var(--line); - padding-top: var(--space-3); -} -.typeface-head .muted { - font-size: var(--step--1); -} -.specimen { - margin-top: var(--space-4); - font-size: 1.1875rem; - line-height: 1.9; - max-width: 46em; -} -.specimen-display { - font-size: clamp(1.75rem, 1.2rem + 3vw, 3.25rem); - line-height: 1.45; - max-width: 22em; - font-weight: 600; -} - -/* ── 조판 시험대 ──────────────────────────────────────────────────────── */ - -.bench-lead { - max-width: 36em; - margin-top: var(--space-3); -} -.bench { - margin-top: var(--space-5); - display: grid; - grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); - gap: var(--space-5) var(--space-7); - align-items: start; -} -.bench-controls { - display: grid; - gap: var(--space-4); -} -.control-head { - display: flex; - justify-content: space-between; - align-items: baseline; -} -.control label { - font-size: var(--step--1); - color: var(--ink-muted); -} -.control output { - font-variant-numeric: tabular-nums; - color: var(--ink); -} -input[type="range"] { - width: 100%; - accent-color: var(--accent); -} -.btn-reset { - justify-self: start; - background: none; - border: 1px solid var(--line); - border-radius: var(--radius); - color: var(--ink-muted); - font-size: var(--step--1); - padding: 0.4rem 0.9rem; - cursor: pointer; - transition: border-color var(--dur-quick) var(--ease-soft), - color var(--dur-quick) var(--ease-soft); -} -.btn-reset:hover { - border-color: var(--accent); - color: var(--ink); -} -.bench-sample { - border: 1px solid var(--line); - background: var(--surface); - border-radius: var(--radius); - padding: var(--space-5); - max-width: 46em; -} - -/* ── 활자 케이스 ──────────────────────────────────────────────────────── */ - -.case-grid { - display: grid; - grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); - gap: var(--space-6) var(--space-7); - align-items: start; -} -.case-photo img, -.case-photo-b img { - display: block; - width: 100%; - height: auto; - border-radius: var(--radius); -} -.case-photo figcaption, -.case-photo-b figcaption { - font-size: var(--step--1); - color: var(--ink-faint); - margin-top: var(--space-2); -} -.case-text h2 { - margin-bottom: var(--space-4); -} -.case-photo-b { - margin-top: var(--space-5); - max-width: 34rem; -} - -/* ── 구입 ─────────────────────────────────────────────────────────────── */ - -.order-grid { - display: grid; - grid-template-columns: minmax(0, 5fr) minmax(0, 6fr) auto; - gap: var(--space-5) var(--space-7); - align-items: start; -} -.order-grid .muted { - margin-top: var(--space-3); - max-width: 26em; -} -.order-rows { - border-top: 1px solid var(--line); -} -.order-row { - display: flex; - justify-content: space-between; - align-items: baseline; - gap: var(--space-4); - padding-block: var(--space-3); - border-bottom: 1px solid var(--line); -} -.order-row p { - font-size: var(--step--1); - color: var(--ink-muted); - margin-top: 2px; -} -.order-price { - font-family: var(--font-display); - font-size: var(--step-1); - white-space: nowrap; -} -.btn { - display: inline-block; - background: var(--accent); - color: var(--accent-ink); - font-weight: 500; - text-decoration: none; - padding: 0.75rem 1.5rem; - border-radius: var(--radius); - transition: background var(--dur-quick) var(--ease-soft); -} -.btn:hover { - background: var(--accent-hover); -} - -/* ── 푸터 ─────────────────────────────────────────────────────────────── */ - -.site-foot { - border-top: 1px solid var(--line); - padding-block: var(--space-5); - font-size: var(--step--1); -} -.foot-inner { - display: flex; - justify-content: space-between; - gap: var(--space-3) var(--space-5); - flex-wrap: wrap; -} - -/* ── 등장 모션 ────────────────────────────────────────────────────────── */ - -.reveal { - opacity: 0; - translate: 0 12px; - transition: opacity var(--dur-normal) var(--ease-soft), - translate var(--dur-normal) var(--ease-soft); - transition-delay: var(--delay, 0ms); -} -.reveal.is-in { - opacity: 1; - translate: 0 0; -} -@media (prefers-reduced-motion: reduce) { - .reveal { - opacity: 1; - translate: 0 0; - transition: none; - } -} - -/* ── 반응형 ───────────────────────────────────────────────────────────── */ +.case-grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--space-6) var(--space-7); align-items: start; } +.case-photo, .case-text { min-inline-size: 0; } +.case-photo img, .case-photo-b img { display: block; max-inline-size: 100%; inline-size: 100%; block-size: auto; border-radius: var(--radius); } +.case-photo figcaption, .case-photo-b figcaption { margin-top: var(--space-2); color: var(--ink-faint); font-size: var(--step--1); } +.case-text { max-inline-size: 42rem; } +.case-text h2 { margin-top: var(--space-3); margin-bottom: var(--space-4); } +.case-photo-b { max-inline-size: 34rem; margin-top: var(--space-5); } +.order-grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr) auto; gap: var(--space-5) var(--space-7); align-items: center; } +.order-grid > * { min-inline-size: 0; } +.order-grid h2 { margin-top: var(--space-3); } +.order-grid .muted { max-inline-size: 28rem; margin-top: var(--space-3); } +.order-rows { border-top: 1px solid var(--line); } +.order-row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); padding-block: var(--space-3); border-bottom: 1px solid var(--line); } +.order-row p { margin-top: var(--space-1); color: var(--ink-muted); font-size: var(--step--1); } +.order-price { flex: 0 0 auto; color: var(--ink) !important; font-family: var(--font-display); font-size: var(--step-1) !important; white-space: nowrap; } +.btn { display: inline-flex; align-items: center; justify-content: center; min-block-size: 2.75rem; border: 1px solid var(--accent); border-radius: var(--radius); background: var(--accent); color: var(--accent-ink); font-weight: 600; padding-inline: 1.25rem; text-decoration: none; } +.btn:hover { background: var(--accent-hover); } +.site-foot { border-top: 1px solid var(--line); padding-block: var(--space-5); font-size: var(--step--1); } +.foot-inner { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-3) var(--space-5); } @media (max-width: 900px) { - .hero-side, - .bench, - .case-grid, - .order-grid { - grid-template-columns: 1fr; - gap: var(--space-5); - } - .case-photo { - max-width: 24rem; - } - .hero, - .section { - padding-block: var(--space-6); - } + .hero-intro, .section-head, .bench-heading, .bench, .case-grid, .order-grid { grid-template-columns: 1fr; } + .hero-intro, .bench-heading { gap: var(--space-4); } + .hero-note { max-inline-size: 34rem; } + .typeface { grid-template-columns: 1fr; gap: var(--space-3); } + .bench { gap: var(--space-5); } + .case-photo { max-inline-size: 30rem; } } @media (max-width: 560px) { - .head-inner { - flex-direction: column; - align-items: flex-start; - gap: var(--space-1); - } - .nav { - width: 100%; - justify-content: space-between; - } - .order-row { - flex-direction: column; - align-items: flex-start; - gap: var(--space-1); - } + .head-inner { display: grid; gap: 0; min-block-size: 0; padding-block: var(--space-3); } + .nav { justify-content: space-between; gap: var(--space-2); max-inline-size: 100%; margin-top: var(--space-1); overflow-x: auto; } + .nav a { flex: 0 0 auto; padding-block: var(--space-1); } + .hero { padding-block: var(--space-6); } + .hero h1 { font-size: clamp(2.5rem, 11vw, 4rem); overflow-wrap: anywhere; word-break: normal; } + .hero-specimen { font-size: min(30vw, 9.5rem); letter-spacing: -0.12em; } + .stage-footer, .order-row { align-items: flex-start; flex-direction: column; } + .type-status { text-align: start; } + .section { padding-block: var(--space-7); } +} +@media (prefers-reduced-motion: reduce) { + html { scroll-behavior: auto; } + *, *::before, *::after { transition-duration: 0.01ms !important; } } diff --git a/apps/site/public/work/font-foundry/styles/tokens.css b/apps/site/public/work/font-foundry/styles/tokens.css index 5baa424..d958822 100644 --- a/apps/site/public/work/font-foundry/styles/tokens.css +++ b/apps/site/public/work/font-foundry/styles/tokens.css @@ -1,38 +1,22 @@ -/* ========================================================================== - 한성활자연구소 — 디자인 토큰 - 프리셋: editorial-archival - 컨셉 한 문장: 글자가 상품이다 — 첫 화면부터 스페시멘, 나머지는 물성과 눈금. - 레퍼런스: dogfood-foundry(파운드리 계열 구조 관찰 — 별도 파일). 방향: - 스페시멘은 Lorem 이 아니라 실제 문장, 테스터는 즉시 반영. - 하지 않은 것: Lorem ipsum, 서체 카드 3열, '구독하면 웨이트 전부' 마케팅, - 책등 세로 내비. 이유: 활자는 보여주는 것이지 포장하는 게 아니다. - 강조 벽돌빨강 #A63D2F — CTA에만. 배경 채움은 버튼 하나 뿐. - ========================================================================== */ - :root { - /* 타입 — 스페시멘 글리프는 스케일 밖(20vw). 본문 17px. */ - --step--1: 0.8125rem; /* 13px */ - --step-0: 1.0625rem; /* 17px — 본문 (조판을 파는 페이지) */ - --step-1: 1.25rem; /* 20px */ - --step-2: clamp(1.375rem, 1.2rem + 0.9vw, 1.75rem); /* 28px */ - --step-3: clamp(2rem, 1.4rem + 3vw, 3.25rem); /* 52px */ - - --leading-tight: 1.3; - --leading-normal: 1.85; - - --font-display: "MaruBuri", serif; - --font-body: "Pretendard Variable", Pretendard, -apple-system, sans-serif; - - --surface: #f5f1e8; /* 따뜻한 중성 바탕 */ + --step--1: 0.8125rem; + --step-0: 1.0625rem; + --step-1: 1.25rem; + --step-2: clamp(1.75rem, 1.3rem + 1.5vw, 2.5rem); + --step-3: clamp(2.5rem, 1.6rem + 4vw, 5rem); + --leading-tight: 1.08; + --leading-normal: 1.8; + --font-display: "MaruBuri", Georgia, serif; + --font-body: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, sans-serif; + --surface: #f5f1e8; --surface-raised: #ede7d9; --ink: #26221d; - --ink-muted: #6a635a; - --ink-faint: #948b7c; + --ink-muted: #625b52; + --ink-faint: #756c60; --line: #d9d1bf; - --accent: #a63d2f; /* 강조 */ + --accent: #a63d2f; --accent-ink: #f5f1e8; - --accent-hover: #8a3225; /* 강조 호버 */ - + --accent-hover: #8a3225; --space-1: 0.25rem; --space-2: 0.5rem; --space-3: 1rem; @@ -40,14 +24,12 @@ --space-5: 2.5rem; --space-6: 4rem; --space-7: 6rem; - --space-8: clamp(4.5rem, 3rem + 6vw, 8.5rem); - + --space-8: clamp(5rem, 3rem + 7vw, 10rem); --radius: 2px; - - --container: 1120px; - --pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4rem); - + --container: 76rem; + --container-wide: 106.25rem; + --pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4.5rem); + --pad-wide: clamp(1.25rem, 1vw + 1rem, 4rem); --dur-quick: 160ms; - --dur-normal: 420ms; --ease-soft: cubic-bezier(0.25, 1, 0.35, 1); } diff --git a/apps/site/public/work/form-architecture/design.md b/apps/site/public/work/form-architecture/design.md new file mode 100644 index 0000000..1decf4c --- /dev/null +++ b/apps/site/public/work/form-architecture/design.md @@ -0,0 +1,43 @@ +# 스튜디오 폼 — 디자인 결정 기록 + +## 0. 브리프와 범위 + +- 무엇을: 빛·비례·구조를 사진과 도면으로 함께 읽는 가상 공간건축 포트폴리오 예시. +- 누구에게: 건축 작업의 기록 방식을 살피고 프로젝트 인덱스로 이동하려는 방문자. +- 제약: 기존 흑백 사진·도면과 Pretendard를 보존한다. 실제 수행 이력·연락처로 오인될 수 없게 가상 사례와 생성 이미지라는 사실을 가까이에 표기한다. 기본 언어는 ko-KR 한국어이며, 영문은 고유 표기 FORM만 사용한다. + +경로: **연장**. 정적 HTML의 토큰·콘텐츠 구조를 재구성했으며 이 문서가 이후 작업의 국소 기준이 된다. + +## 1. 조사와 관찰 + +| 출처 | 관찰 | 적용 범위 | +| --- | --- | --- | +| [David Chipperfield Architects — Projects](https://davidchipperfield.com/projects) | 프로젝트 사진·모형과 그 아래 정렬된 메타데이터가 탐색의 위계를 소유하고, Grid/List/Filter는 보조 탐색으로 물러난다. | 사진을 첫 화면의 주 장면으로 두고, 인덱스는 번호·이름·위치·연도를 정렬한다. | +| [Klim Type Foundry](https://klim.co.nz/) | 글자와 작업물이 표면을 주도하고 인터페이스는 뒤로 물러난다. | 큰 헤드라인, 얇은 선, 흑백 사진의 우선순위. | +| [Carbon 2x Grid](https://carbondesignsystem.com/elements/2x-grid/usage/) | 넓은 표면과 읽기 표면은 분리된 레일을 가져야 한다. | 최대 1800px stage와 1280px 정보 레일, 720px 본문 measure. | + +표현을 복제하지 않았다. 이 페이지의 고유한 결합은 `제목 → 실제로 보이는 사진 → 도면의 이유`라는 건축 기록 순서다. + +## 2. 수정 전 문제와 결정 + +- 1440px 첫 화면에서 소개 문단이 약 80px 열에 갇혀 단어마다 줄바꿈됐고, 사진과 인덱스 행동이 첫 화면에 없었다. +- `설립 연도 예시`, `팀 구성 예시`처럼 작업 판독에 기여하지 않는 가상 수치를 제거했다. +- 기존 인덱스의 여섯 항목은 모두 같은 도면으로 연결됐지만 그 관계가 설명되지 않았다. 이제 목록 직전에 같은 가상 도면을 다른 관점에서 참조한다는 사실을 고지한다. +- 기존 생성 이미지는 유지하고 각 사진 캡션에 `생성 이미지 · 실제 프로젝트 사진 아님`을 남긴다. + +## 3. 토큰과 레이아웃 계약 + +- 표면 `#fdfdfb`, 전경 `#171b19`, 중립 선 `#d8dbd7`, 보조 글자 `#5b615d`. +- 본문 16px 이상, 캡션 12px 이상, 본문 줄간 1.75, 읽기 폭 720px. +- `h1: clamp(2.5rem, 5.5vw, 5.5rem)`; Pretendard 단일 패밀리. +- 1440px 정보 레일 최대 1280px, stage 최대 1800px; 2560px에서는 사진만 stage에 머물고 산문은 720px 내에서 읽힌다. +- 모바일 gutter는 20px, 320px에서 16px. 이미지의 원본 비율을 유지하며 `object-fit: cover`는 슬롯에서 정한 비율 안의 편집에만 사용한다. + +성능 예산: 첫 화면 JS 0KB / 첫 인터랙션은 정적 링크 / 폰트 패밀리 1개. 이미지 생성·추가는 하지 않는다. + +## 4. 검증 추적 + +- 320, 390, 768, 1440, 1920, 2560px에서 문서 및 요소 가로 오버플로, h1/본문 가시성, 레일 상한, 이미지 로드, 탭 순서를 점검한다. +- `prefers-reduced-motion`과 효과 제거 상태에서도 콘텐츠가 같은 순서로 읽히는지 점검한다. +- 200% 텍스트 확대에서 본문·CTA가 사라지지 않는지 점검한다. +- 실행 결과와 화면 캡처는 `outputs/design-expertise-20260912/form/`에 남긴다. diff --git a/apps/site/public/work/form-architecture/index.html b/apps/site/public/work/form-architecture/index.html index ad94f91..2aaab07 100644 --- a/apps/site/public/work/form-architecture/index.html +++ b/apps/site/public/work/form-architecture/index.html @@ -4,300 +4,91 @@ 스튜디오 폼 · 공간건축연구소 - + - + - - - - - + + + + - - - - - - - -
-
- 스튜디오 폼 FORM +
-
-
-
-

- 공간건축연구소 · 서울 · 2014년 설립 -

-

- 형태는 침묵하고
치수가 태도를 증명한다 -

-

- 폼은 공간을 꾸미지 않습니다. 벽이 서는 이유, 창이 열리는 방향, 보가 - 건너뛰는 길이를 설계합니다. 아름다움은 그 결정들이 모였을 때의 - 부산물입니다 — 그래서 우리의 홍보는 도면과 치수로 합니다. -

-
-
-
설립
-
2014년
-
-
-
구성
-
건축사 4 · 연구원 3
-
-
-
영역
-
주거 · 미술관 · 재생
-
-
+
+
+

가상 공간건축 포트폴리오 · 설계 기록 형식

+

공간을 짓는
명료한 질서.

+
+
+

빛이 머무는 방향과 구조가 건너는 거리를 함께 읽습니다. 사진은 결과를, 도면은 그 결과가 만들어진 이유를 남깁니다.

+ 프로젝트 인덱스 보기 +
+
+ 긴 수평창이 난 콘크리트 파빌리온에 벽 그림자가 지는 모습 +
폼 하우스 · 가평 · 2025생성 이미지 · 실제 프로젝트 사진 아님
+
+

이 페이지의 프로젝트명·연도·도면·이미지는 공간 설계 기록의 형식을 설명하기 위한 가상 사례입니다.

- -
-
-

프로젝트 인덱스

+
+
-
-
-
- - - - - - - - - - - - - - 14,400 - 9,600 - - - - 거실 - - 주방·식당 - 서비스 - 현관 - - - - - N - - -

폼 하우스 · 1층 평면도 · mm

-
- +
+

도면으로 읽는 장면

폼 하우스, 가평

-

- 대지 남쪽의 소나무군을 지우지 않기 위해 집은 길게 눕고, 마당은 집 - 안쪽으로 들어왔습니다. 구조는 9.6m 축 한 번으로 건너뛰어 거실에 기둥이 - 없습니다. -

-

- 벽체는 단열 성능의 요구치에서 두께가 정해졌습니다 — 마감 재료를 먼저 - 고르고 두께를 맞추지 않았습니다. 창은 남향 긴면 하나, 동쪽 마당 면 - 하나. 그 두 개로 이 집의 하루가 결정됩니다. -

-
-
-
구조
-
RC 라멘 · 9.6m 스팬
-
-
-
면적
-
132.4 ㎡ (40.0평)
-
-
-
창개구
-
남측 14.4m · 동측 4.2m
-
-
-
줄공사
-
2025-04 – 2026-01
-
-
+

대지 남쪽의 소나무군을 지우지 않기 위해 집은 길게 눕고, 마당은 집 안쪽으로 들어왔습니다. 구조는 9.6m 축 한 번으로 건너뛰어 거실에 기둥이 없습니다.

+

벽체는 단열 성능의 요구치에서 두께가 정해졌습니다. 창은 남향 긴면 하나, 동쪽 마당 면 하나. 그 두 개로 이 집의 하루가 결정됩니다.

+
구조
RC 라멘 · 9.6m 스팬
면적
132.4 ㎡ (40.0평)
창개구
남측 14.4m · 동측 4.2m
공사 기간
2025-04 – 2026-01
+
+
+ + + 14,4009,600 + 거실주방·식당서비스현관N + +

폼 하우스 · 1층 평면도 · 가상 설계 도면 예시 · mm

-
-
-

사진

+

사진 시퀀스

빛의 방향, 재료의 밀도, 비례의 검토를 순서대로 기록합니다.

-
- 긴 수평창이 낸 콘크리트 파빌리온에 벽 그림자가 지는 모습 -
폼 하우스 · 남측 · 준공 후 첫 여름
-
-
- 천창으로 한 줄기 빛이 사선으로 들어오는 미장 마감 전시실 -
백령도 해녀기념관 · 제1전시실
-
-
- 스터디 모델 — 직육면체를 쌓은 백색 석고 모형에 측광이 지는 모습 -
스터디 모델 07 · 석고
-
+
긴 수평창이 낸 콘크리트 파빌리온에 벽 그림자가 지는 모습
폼 하우스 · 남측 · 준공 후 첫 여름생성 이미지 · 실제 프로젝트 사진 아님
+
천창으로 한 줄기 빛이 사선으로 들어오는 미장 마감 전시실
백령도 해녀기념관 · 제1전시실생성 이미지 · 실제 프로젝트 사진 아님
+

비례의 검토

빛과 재료의 표정은 사진으로, 공간의 관계는 도면으로 살펴봅니다.

+
직육면체를 쌓은 백색 석고 모형에 측광이 지는 모습
스터디 모델 07 · 석고생성 이미지 · 실제 프로젝트 사진 아님
- -
-
-

연락

-
- 서울 종로구 자하문로 00길 00, 3층
- studio@form.kr -
-

- 신규 프로젝트는 분기에 두 건만 받습니다. 대지와 예산의 조건을 적어 - 보내주시면, 가능 여부를 2주 안에 답합니다. -

-
-
+

구성 안내

가상 공간건축 포트폴리오
실제 연락처를 제공하지 않습니다.

이 페이지는 도면·사진·사양을 한 흐름으로 읽는 포트폴리오 형식의 예시입니다. 신규 프로젝트 상담이나 견적을 접수하지 않습니다.

- - - - + diff --git a/apps/site/public/work/form-architecture/styles/main.css b/apps/site/public/work/form-architecture/styles/main.css index fb70a83..43ea20e 100644 --- a/apps/site/public/work/form-architecture/styles/main.css +++ b/apps/site/public/work/form-architecture/styles/main.css @@ -1,497 +1,91 @@ -@import url("./tokens.css"); +@import url("./tokens.css?v=3"); -/* ========================================================================== - 스튜디오 폼 — swiss-minimal. 판맞춤 도면 묶음. - ========================================================================== */ +*, *::before, *::after { box-sizing: border-box; } +* { margin: 0; } +html { background: var(--surface); scroll-behavior: smooth; -webkit-text-size-adjust: 100%; } +body { min-width: 0; background: var(--surface); color: var(--ink); font-family: var(--font-body); font-size: var(--step-0); line-height: var(--leading-normal); -webkit-font-smoothing: antialiased; } +img { display: block; max-width: 100%; height: auto; } +h1, h2 { font-weight: 600; line-height: var(--leading-tight); letter-spacing: -.025em; word-break: keep-all; overflow-wrap: break-word; text-wrap: balance; } +p, dd, dt, address, figcaption { word-break: keep-all; overflow-wrap: break-word; text-wrap: pretty; } +a { color: inherit; } +a:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; } +.rail, .wide-rail, .stage { width: min(100% - 2 * var(--pad), var(--rail)); margin-inline: auto; } +.wide-rail, .stage { width: min(100% - 2 * var(--pad), var(--stage)); } +.skip { position: absolute; left: -999px; top: 0; z-index: 100; background: var(--ink); color: var(--surface); padding: var(--space-3); } +.skip:focus { left: 0; } -*, -*::before, -*::after { - box-sizing: border-box; -} -* { - margin: 0; -} +.site-head { position: sticky; top: 0; z-index: 20; min-height: var(--header-h); background: color-mix(in srgb, var(--surface) 96%, transparent); border-bottom: 1px solid var(--line); backdrop-filter: blur(8px); } +.head-inner { min-height: var(--header-h); display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); } +.wordmark { font-weight: 600; text-decoration: none; letter-spacing: -.02em; } +.wordmark span { margin-left: var(--space-1); color: var(--ink-faint); font-weight: 400; } +.nav { display: flex; gap: clamp(1rem, 2vw, 2.5rem); font-size: var(--step--1); } +.nav a { display: inline-flex; align-items: center; min-height: 2.75rem; text-decoration: none; color: var(--ink-muted); border-bottom: 1px solid transparent; transition: color var(--dur-quick) var(--ease), border-color var(--dur-quick) var(--ease); } +.nav a:hover { color: var(--ink); border-color: var(--ink); } -html { - -webkit-text-size-adjust: 100%; - scroll-behavior: smooth; - background: var(--surface); -} -@media (prefers-reduced-motion: reduce) { - html { - scroll-behavior: auto; - } -} +.hero { padding: var(--space-6) 0 var(--space-5); border-bottom: 1px solid var(--line); } +.hero-top { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(18rem, .7fr); gap: var(--space-6); align-items: end; margin-bottom: var(--space-5); } +.eyebrow { font-size: var(--step--1); line-height: 1.5; color: var(--ink-muted); letter-spacing: .08em; } +.hero-title .eyebrow { margin-bottom: var(--space-3); } +h1 { font-size: var(--step-display); max-width: 10em; } +.hero-intro { max-width: 28rem; padding-bottom: var(--space-1); } +.hero-intro p { color: var(--ink-muted); } +.index-link { display: inline-flex; align-items: center; gap: var(--space-2); min-height: 2.75rem; margin-top: var(--space-3); font-weight: 500; text-underline-offset: .35em; text-decoration-thickness: 1px; } +.index-link span { font-size: 1.25em; } +.hero-image img { width: 100%; aspect-ratio: 2.35 / 1; object-fit: cover; object-position: center 58%; filter: grayscale(1); } +figcaption { display: flex; justify-content: space-between; gap: var(--space-3); margin-top: var(--space-2); color: var(--ink-faint); font-size: var(--step--1); line-height: 1.55; } +.hero-caption { margin-top: var(--space-3); color: var(--ink-muted); font-size: var(--step--1); } +.hero-caption p { max-width: var(--measure); } -body { - background: var(--surface); - color: var(--ink); - font-family: var(--font-body); - font-size: var(--step-0); - line-height: var(--leading-normal); - -webkit-font-smoothing: antialiased; -} +.section { padding-block: var(--space-7); } +.section-head { display: grid; grid-template-columns: minmax(14rem, .7fr) minmax(0, 1fr); gap: var(--space-5); align-items: baseline; margin-bottom: var(--space-5); border-top: 1px solid var(--line-strong); padding-top: var(--space-3); } +.section-head h2 { font-size: var(--step-1); letter-spacing: .03em; } +.section-head p { max-width: 40rem; color: var(--ink-muted); font-size: var(--step--1); } +.index { list-style: none; padding: 0; border-top: 1px solid var(--line-strong); } +.index li { border-bottom: 1px solid var(--line); } +.index a { display: grid; grid-template-columns: 3.5rem minmax(0, 1fr) minmax(8rem, .35fr) 4rem; gap: var(--space-3); align-items: baseline; padding: 1.15rem var(--space-2); text-decoration: none; transition: background var(--dur-quick) var(--ease), color var(--dur-quick) var(--ease); } +.index a:hover { background: var(--ink); color: var(--surface); } +.idx-no, .idx-loc, .idx-year { font-size: var(--step--1); color: var(--ink-faint); } +.idx-name { font-size: var(--step-1); font-weight: 500; } +.idx-year { font-variant-numeric: tabular-nums; text-align: right; } +.index a:hover .idx-no, .index a:hover .idx-loc, .index a:hover .idx-year { color: inherit; } -:where(h1, h2, h3) { - font-weight: 600; - line-height: var(--leading-tight); - letter-spacing: -0.01em; - word-break: keep-all; - overflow-wrap: break-word; - text-wrap: balance; -} -h1 { - font-size: var(--step-3); -} -h2 { - font-size: var(--step-2); -} -:where(p, li, dd, dt, th, td, address, figcaption) { - word-break: keep-all; - overflow-wrap: break-word; - line-height: var(--leading-normal); - text-wrap: pretty; -} +.drawing { border-top: 1px solid var(--line); background: var(--surface-raised); } +.drawing-grid { display: grid; grid-template-columns: minmax(17rem, .82fr) minmax(20rem, 1fr); gap: clamp(2.5rem, 6vw, 8rem); align-items: start; } +.drawing-notes { max-width: var(--measure); } +.drawing-notes .eyebrow { margin-bottom: var(--space-3); } +.drawing-notes h2 { font-size: var(--step-2); margin-bottom: var(--space-4); } +.drawing-notes p + p { margin-top: var(--space-3); } +.drawing-spec { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); margin-top: var(--space-5); padding-top: var(--space-4); border-top: 1px solid var(--line); } +.drawing-spec dt { font-size: var(--step--1); color: var(--ink-faint); } +.drawing-spec dd { margin-top: var(--space-1); font-size: var(--step--1); font-weight: 500; font-variant-numeric: tabular-nums; } +.drawing-stage { position: sticky; top: calc(var(--header-h) + var(--space-3)); } +.drawing-stage svg { display: block; width: 100%; height: auto; border: 1px solid var(--line); background: var(--surface); } +.wall { fill: none; stroke: var(--ink); stroke-width: 2; } +.wall.inner { stroke-width: 1.25; } +.door, .dim path, .north path { fill: none; stroke: var(--ink-muted); stroke-width: 1; } +.column { fill: var(--ink); } +.dim text, .north text { fill: var(--ink-faint); font-size: 12px; } +.room-label text { fill: var(--ink-muted); font-size: 13px; } +.drawing-caption { display: block; } -a { - color: inherit; -} -.link { - text-decoration: none; - border-bottom: 1px solid var(--line-strong); - transition: border-color var(--dur-quick) var(--ease-soft); -} -.link:hover { - border-color: var(--ink); -} +.photo { border-top: 1px solid var(--line); } +.photo-grid { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(15rem, .55fr); gap: var(--space-5); } +.ph-wide { grid-column: 1 / -1; } +.ph-wide img { width: 100%; aspect-ratio: 2.35 / 1; object-fit: cover; filter: grayscale(1); } +.ph-narrow { align-self: start; } +.ph-narrow img { width: 100%; aspect-ratio: 3 / 4; object-fit: cover; filter: grayscale(1); } +.ph-model { align-self: end; } +.ph-model img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; filter: grayscale(1); } +.ph-note { align-self: end; border-top: 1px solid var(--line-strong); padding-top: var(--space-3); max-width: 30rem; } +.ph-note p:last-child { margin-top: var(--space-3); font-size: var(--step-1); line-height: 1.55; } -:focus-visible { - outline: 2px solid var(--ink); - outline-offset: 3px; -} +.contact { border-top: 1px solid var(--line); } +.contact-grid { display: grid; grid-template-columns: .7fr 1fr 1.2fr; gap: var(--space-5); align-items: start; } +.contact h2 { font-size: var(--step-1); letter-spacing: .03em; } +.contact address, .contact p { color: var(--ink-muted); font-style: normal; } +.site-foot { border-top: 1px solid var(--line-strong); padding-block: var(--space-4); font-size: var(--step--1); color: var(--ink-muted); } +.foot-inner { display: flex; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; } -.skip { - position: absolute; - left: -9999px; - top: 0; - background: var(--ink); - color: var(--surface); - padding: var(--space-3) var(--space-4); - z-index: 100; -} -.skip:focus { - left: 0; -} - -.wrap { - width: min(100% - 2 * var(--pad-inline), var(--container)); - margin-inline: auto; -} - -/* ── 판맞춤 표시 — 넓은 화면에서만, 내용과 겹치지 않는 곳에 ──────────── */ - -.corner { - position: fixed; - width: 18px; - height: 18px; - z-index: 40; - pointer-events: none; -} -.corner-tl { - top: 10px; - left: 10px; - border-top: 1px solid var(--line-strong); - border-left: 1px solid var(--line-strong); -} -.corner-tr { - top: 10px; - right: 10px; - border-top: 1px solid var(--line-strong); - border-right: 1px solid var(--line-strong); -} -.corner-bl { - bottom: 10px; - left: 10px; - border-bottom: 1px solid var(--line-strong); - border-left: 1px solid var(--line-strong); -} -.corner-br { - bottom: 10px; - right: 10px; - border-bottom: 1px solid var(--line-strong); - border-right: 1px solid var(--line-strong); -} -@media (max-width: 900px) { - .corner { - display: none; - } -} - -/* ── 헤더 ─────────────────────────────────────────────────────────────── */ - -.site-head { - position: sticky; - top: 0; - z-index: 50; - background: color-mix(in srgb, var(--surface) 90%, transparent); - backdrop-filter: blur(6px); - border-bottom: 1px solid var(--line); -} -.head-inner { - display: flex; - align-items: baseline; - justify-content: space-between; - gap: var(--space-4); - padding-block: var(--space-3); -} -.wordmark { - font-size: var(--step-1); - font-weight: 600; - text-decoration: none; -} -.wordmark-en { - color: var(--ink-faint); - font-weight: 400; - margin-left: var(--space-1); -} -.nav { - display: flex; - gap: clamp(var(--space-3), 2vw, var(--space-5)); - font-size: var(--step--1); -} -.nav a { - text-decoration: none; - color: var(--ink-muted); - border-bottom: 1px solid transparent; - padding-block: 2px; - transition: color var(--dur-quick) var(--ease-soft), - border-color var(--dur-quick) var(--ease-soft); -} -.nav a:hover { - color: var(--ink); - border-color: var(--ink); -} - -/* ── 서문 ─────────────────────────────────────────────────────────────── */ - -.hero { - padding-block: var(--space-8) var(--space-7); - border-bottom: 1px solid var(--line); -} -.hero-meta { - font-size: var(--step--1); - color: var(--ink-muted); - margin-bottom: var(--space-5); -} -.hero-statement { - margin-top: var(--space-5); - max-width: 30em; -} -.hero-facts { - margin-top: var(--space-6); - display: flex; - flex-wrap: wrap; - gap: var(--space-4) var(--space-7); - border-top: 1px solid var(--line); - padding-top: var(--space-4); -} -.hero-facts dt { - font-size: var(--step--1); - color: var(--ink-faint); -} -.hero-facts dd { - font-weight: 500; -} - -/* ── 인덱스 ───────────────────────────────────────────────────────────── */ - -.section { - padding-block: var(--space-8); -} -.index-h { - font-size: var(--step--1); - font-weight: 500; - color: var(--ink-muted); - letter-spacing: 0.2em; -} -.index { - list-style: none; - margin-top: var(--space-3); - padding: 0; - border-top: 1px solid var(--line-strong); -} -.index-row a { - display: grid; - grid-template-columns: minmax(0, 1fr) auto auto; - gap: var(--space-4) var(--space-6); - align-items: baseline; - padding-block: var(--space-3); - border-bottom: 1px solid var(--line); - text-decoration: none; - transition: background var(--dur-quick) var(--ease-soft); -} -.index-row a:hover { - background: var(--ink); - color: var(--surface); -} -.idx-name { - font-size: var(--step-1); - font-weight: 500; -} -.idx-loc { - color: var(--ink-muted); - font-size: var(--step--1); -} -.index-row a:hover .idx-loc { - color: inherit; -} -.idx-year { - font-variant-numeric: tabular-nums; - color: var(--ink-faint); -} -.index-row a:hover .idx-year { - color: inherit; -} - -/* ── 도면(스티키) ─────────────────────────────────────────────────────── */ - -.drawing { - background: var(--surface); -} -.drawing-grid { - display: grid; - grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); - gap: var(--space-6) var(--space-7); -} -.drawing-stage { - position: sticky; - top: calc(var(--space-5) + 56px); - align-self: start; -} -.drawing-stage svg { - display: block; - width: 100%; - height: auto; - border: 1px solid var(--line); - background: var(--surface-bright); -} -.wall { - fill: none; - stroke: var(--ink); - stroke-width: 2; -} -.wall.inner { - stroke-width: 1.25; -} -.door { - fill: none; - stroke: var(--ink-muted); - stroke-width: 1; - stroke-dasharray: none; -} -.column { - fill: var(--ink); -} -.dim path { - fill: none; - stroke: var(--line-strong); - stroke-width: 1; -} -.dim text { - font-size: 12px; - fill: var(--ink-faint); - font-variant-numeric: tabular-nums; -} -.room-label text { - font-size: 13px; - fill: var(--ink-muted); -} -.north path { - fill: none; - stroke: var(--ink); - stroke-width: 1.25; -} -.north text { - font-size: 12px; - fill: var(--ink-muted); -} -.drawing-caption { - margin-top: var(--space-2); - font-size: var(--step--1); - color: var(--ink-faint); -} - -@media (prefers-reduced-motion: no-preference) { - @supports (animation-timeline: view()) { - .wall, - .wall.inner { - stroke-dasharray: 3400; - stroke-dashoffset: 3400; - animation: draw 1.4s linear both; - animation-timeline: --drawing; - animation-range: entry 10% contain 55%; - } - @keyframes draw { - to { - stroke-dashoffset: 0; - } - } - } -} - -.drawing-notes h2 { - font-size: var(--step-2); -} -.drawing-notes p + p { - margin-top: var(--space-4); -} -.drawing-spec { - margin-top: var(--space-5); - display: grid; - grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); - gap: var(--space-3) var(--space-5); - border-top: 1px solid var(--line); - padding-top: var(--space-4); -} -.drawing-spec dt { - font-size: var(--step--1); - color: var(--ink-faint); -} -.drawing-spec dd { - margin-top: 2px; - font-weight: 500; - font-variant-numeric: tabular-nums; -} - -/* ── 사진 ─────────────────────────────────────────────────────────────── */ - -.photo { - border-top: 1px solid var(--line); -} -.photo h2 { - font-size: var(--step--1); - font-weight: 500; - color: var(--ink-muted); - letter-spacing: 0.2em; -} -.photo-grid { - margin-top: var(--space-4); - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: var(--space-4); -} -.ph { - display: block; -} -.ph img { - display: block; - width: 100%; - height: auto; - filter: grayscale(1); /* 사진 파일이 흑백이 아니어도 페이지는 흑백이다 */ -} -.ph-a { - grid-column: 1 / -1; -} -.ph figcaption { - margin-top: var(--space-2); - font-size: var(--step--1); - color: var(--ink-faint); -} - -/* ── 연락 ─────────────────────────────────────────────────────────────── */ - -.contact { - border-top: 1px solid var(--line); -} -.contact-grid { - display: grid; - grid-template-columns: auto minmax(0, 1fr) minmax(0, 1fr); - gap: var(--space-5) var(--space-7); - align-items: start; -} -.contact address { - font-style: normal; - color: var(--ink-muted); -} -.contact-note { - color: var(--ink-muted); - max-width: 26em; -} - -/* ── 푸터 ─────────────────────────────────────────────────────────────── */ - -.site-foot { - border-top: 1px solid var(--line-strong); - padding-block: var(--space-4); - font-size: var(--step--1); -} -.foot-inner { - display: flex; - justify-content: space-between; - gap: var(--space-3) var(--space-5); - flex-wrap: wrap; -} -.foot-meta { - color: var(--ink-faint); -} - -/* ── 등장 모션 ────────────────────────────────────────────────────────── */ - -.reveal { - opacity: 0; - translate: 0 10px; - transition: opacity var(--dur-normal) var(--ease-soft), - translate var(--dur-normal) var(--ease-soft); - transition-delay: var(--delay, 0ms); -} -.reveal.is-in { - opacity: 1; - translate: 0 0; -} -@media (prefers-reduced-motion: reduce) { - .reveal { - opacity: 1; - translate: 0 0; - transition: none; - } -} - -/* ── 반응형 ───────────────────────────────────────────────────────────── */ - -@media (max-width: 900px) { - .drawing-grid { - grid-template-columns: 1fr; - } - .drawing-stage { - position: static; - } - .contact-grid { - grid-template-columns: 1fr; - } - .hero, - .section { - padding-block: var(--space-6); - } -} -@media (max-width: 560px) { - .head-inner { - flex-direction: column; - align-items: flex-start; - gap: var(--space-1); - } - .nav { - width: 100%; - justify-content: space-between; - } - .index-row a { - grid-template-columns: 1fr auto; - } - .idx-loc { - grid-column: 1; - } - .idx-year { - grid-row: 1; - grid-column: 2; - } -} +@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .nav a, .index a { transition: none; } } +@media (max-width: 48rem) { .hero-top, .drawing-grid { grid-template-columns: 1fr; } .hero-top { gap: var(--space-4); } .hero-intro { max-width: var(--measure); } .drawing-stage { position: static; grid-row: 1; } .drawing-notes { grid-row: 2; } .photo-grid { grid-template-columns: minmax(0, 1fr) minmax(11rem, .72fr); } .contact-grid { grid-template-columns: 1fr; gap: var(--space-3); } } +@media (max-width: 35rem) { .head-inner { padding-block: var(--space-2); align-items: flex-start; flex-direction: column; gap: 0; } .site-head { min-height: auto; } .nav { width: 100%; justify-content: space-between; gap: var(--space-2); } .nav a { min-height: 2.25rem; } .hero { padding-top: var(--space-5); } .hero-image img, .ph-wide img { aspect-ratio: 1.45 / 1; } .section-head { grid-template-columns: 1fr; gap: var(--space-2); } .index a { grid-template-columns: 2.25rem minmax(0, 1fr) auto; gap: var(--space-2); padding-inline: 0; } .idx-loc { grid-column: 2; } .idx-year { grid-column: 3; grid-row: 1; text-align: right; } .photo-grid { grid-template-columns: 1fr; } .ph-note { grid-row: 2; } .ph-narrow { grid-row: 3; max-width: 70%; } .ph-model { grid-row: 4; } .contact { padding-block: var(--space-6); } figcaption { flex-direction: column; gap: 0; } .drawing-spec { grid-template-columns: 1fr; } } diff --git a/apps/site/public/work/form-architecture/styles/tokens.css b/apps/site/public/work/form-architecture/styles/tokens.css index 83f3f54..1de8ebf 100644 --- a/apps/site/public/work/form-architecture/styles/tokens.css +++ b/apps/site/public/work/form-architecture/styles/tokens.css @@ -1,52 +1,34 @@ -/* ========================================================================== - 스튜디오 폼 — 디자인 토큰 - 프리셋: swiss-minimal - 컨셉 한 문장: 판맞춤 도면 묶음 — 색이 아니라 선과 치수가 말한다. - 레퍼런스: R1 johnpawson.com(실측: 724px 고정폭, 전 태그 동일 크기+색으로만 - 위계, 유일한 구분선 1px, 프로젝트는 제목/위치/연도 텍스트 행) - R2 cereal(웜 크림 3단 모노크롬 → 우리는 중성 회색으로 번안) - R3 CSS scroll-driven view()+dashoffset(이중 게이트, 폴백=완성 도면) - 하지 않은 것: 색(무채색 100% — 강조색 없음, 링크만 밑줄), radius 전면 0, - 사진 캐러셀, 서비스 아이콘. 이유: 도면의 권위는 선 하나에 있다. - ========================================================================== */ - :root { - /* 타입 — Pawson 교훈: 크기를 다양하게 쓰지 않는다. 위계는 무게와 색으로. */ - --step--1: 0.8125rem; /* 13px — 캡션·치수 */ - --step-0: 1rem; /* 16px — 본문 */ - --step-1: 1.125rem; /* 18px — 소제목(본문과 1단계 차이만) */ - --step-2: 1.375rem; /* 22px — 섹션 표제 */ - --step-3: clamp(1.75rem, 1.3rem + 2.4vw, 2.75rem); /* 44px — 서문 하나만 */ - - --leading-tight: 1.35; - --leading-normal: 1.8; - - --font-body: "Pretendard Variable", Pretendard, -apple-system, sans-serif; - - /* 무채색 3단계 + 선 2단계. 강조색 없음. */ + --font-body: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, sans-serif; --surface: #fdfdfb; - --ink: #111111; - --ink-muted: #555555; - --ink-faint: #8a8a8a; - --line: #d9d9d5; - --line-strong: #999999; /* Pawson의 유일한 구분선 색 */ - --surface-bright: #ffffff; /* index 행 hover 반전의 가장 밝은 단계 */ /* Pawson의 유일한 구분선 색 */ - + --surface-raised: #ffffff; + --ink: #171b19; + --ink-muted: #5b615d; + --ink-faint: #686e6a; + --line: #d8dbd7; + --line-strong: #a7aca7; + --step--1: 0.75rem; + --step-0: 1rem; + --step-1: 1.25rem; + --step-2: clamp(1.75rem, 1.3rem + 1.7vw, 2.5rem); + --step-display: clamp(2.5rem, 5.5vw, 5.5rem); + --leading-tight: 1.12; + --leading-normal: 1.75; + --measure: 45rem; + --rail: 78rem; + --stage: 112.5rem; + --pad: clamp(1.25rem, 1rem + 2vw, 3rem); --space-1: 0.25rem; --space-2: 0.5rem; --space-3: 1rem; --space-4: 1.5rem; --space-5: 2.5rem; - --space-6: 4rem; - --space-7: 6rem; - --space-8: clamp(4.5rem, 3rem + 6vw, 8.5rem); - - --radius: 0; /* 이 페이지에는 모서리 둥금이 없다 */ - - --container: 1080px; - --pad-inline: clamp(1.25rem, 1rem + 2.5vw, 4rem); - + --space-6: clamp(3.5rem, 2.2rem + 4vw, 6rem); + --space-7: clamp(5rem, 3rem + 6vw, 9rem); + --header-h: 4rem; --dur-quick: 140ms; - --dur-normal: 380ms; - --ease-soft: cubic-bezier(0.25, 1, 0.35, 1); + --ease: cubic-bezier(.25, 1, .35, 1); } + +@media (min-width: 120rem) { :root { --rail: 80rem; --stage: 112.5rem; } } +@media (max-width: 20rem) { :root { --pad: 1rem; } } diff --git a/apps/site/src/assets/shots/work-font-foundry-2.png b/apps/site/src/assets/shots/work-font-foundry-2.png index 2c2f18e..effb070 100644 Binary files a/apps/site/src/assets/shots/work-font-foundry-2.png and b/apps/site/src/assets/shots/work-font-foundry-2.png differ diff --git a/apps/site/src/assets/shots/work-font-foundry.png b/apps/site/src/assets/shots/work-font-foundry.png index 5f66817..1897bc8 100644 Binary files a/apps/site/src/assets/shots/work-font-foundry.png and b/apps/site/src/assets/shots/work-font-foundry.png differ diff --git a/apps/site/src/assets/shots/work-form-architecture-2.png b/apps/site/src/assets/shots/work-form-architecture-2.png index 2c41eab..7ae0d38 100644 Binary files a/apps/site/src/assets/shots/work-form-architecture-2.png and b/apps/site/src/assets/shots/work-form-architecture-2.png differ diff --git a/apps/site/src/assets/shots/work-form-architecture.png b/apps/site/src/assets/shots/work-form-architecture.png index 99c2808..7ae0d38 100644 Binary files a/apps/site/src/assets/shots/work-form-architecture.png and b/apps/site/src/assets/shots/work-form-architecture.png differ diff --git a/apps/site/src/components/Landing.astro b/apps/site/src/components/Landing.astro index 0103d89..e66b8c0 100644 --- a/apps/site/src/components/Landing.astro +++ b/apps/site/src/components/Landing.astro @@ -36,6 +36,12 @@ const copiedLabel = lang === "ko" ? "복사했어요" : "Copied"; + + {c.outputs.archiveCta} + + @@ -348,14 +354,22 @@ const copiedLabel = lang === "ko" ? "복사했어요" : "Copied"; .proof-facts > div + div { border-left: var(--line-width) solid var(--line); } - .proof-facts dt { display: grid; gap: var(--space-2); } + .proof-facts dt { + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: var(--space-2); + min-width: 0; + } .proof-value { color: var(--accent); font-size: clamp(2.5rem, 2rem + 2vw, 4rem); font-weight: var(--weight-medium); line-height: 1; + overflow-wrap: anywhere; } + .proof-facts dt > span { overflow-wrap: anywhere; } + .proof-facts dd { margin-top: var(--space-4); color: var(--ink-muted); diff --git a/apps/site/src/components/OutputSpecimens.astro b/apps/site/src/components/OutputSpecimens.astro index e82c419..c93c828 100644 --- a/apps/site/src/components/OutputSpecimens.astro +++ b/apps/site/src/components/OutputSpecimens.astro @@ -213,6 +213,7 @@ const copy = lang === "en" .direction-stage { position: relative; + display: flow-root; aspect-ratio: 2 / 1; overflow: clip; background: var(--surface-soft); @@ -292,10 +293,11 @@ const copy = lang === "en" .direction-axis-3 { right: 5%; bottom: 12%; text-align: right; } .direction-statement { - position: absolute; + position: relative; left: 48%; - top: 48%; width: min(43%, 18rem); + margin-top: 24%; + margin-bottom: 5%; color: var(--ink); font-size: var(--step-1); line-height: 1.38; @@ -637,6 +639,7 @@ const copy = lang === "en" .specimen-tokens, .specimen-proof { grid-column: 1; margin-top: 0; } .direction-stage { aspect-ratio: 16 / 9; } + .direction-statement { margin-top: 27%; } .type-stage { min-height: 24rem; } .proof-stage { grid-template-columns: minmax(0, 1fr); } .viewport-family { min-height: 18rem; border-right: 0; border-bottom: var(--line-width) solid var(--line); } diff --git a/apps/site/src/i18n/landing.ts b/apps/site/src/i18n/landing.ts index f5a9270..d2aded8 100644 --- a/apps/site/src/i18n/landing.ts +++ b/apps/site/src/i18n/landing.ts @@ -120,7 +120,7 @@ export const landingKo = { hero: { h1: "AI 코딩에\n디자인을 더합니다", lead: - "레퍼런스를 조사해 방향을 정하고, 디자인 토큰부터 반응형 구현과 프리플라이트 검증까지 일관된 절차로 연결합니다.", + "웹 디자인 원리와 실제 사례를 조사해, 브랜드에 맞는 방향부터 반응형 구현과 실화면 검증까지 연결합니다.", command: "npx designpaca", primaryCta: "설치 명령 복사", secondaryCta: "산출물 살펴보기", @@ -128,7 +128,7 @@ export const landingKo = { outputs: { h2: "정한 기준이 코드와 검사까지 이어집니다", lead: "레퍼런스에서 고른 원칙은 결정 기록에 남고, 디자인 토큰과 구현 규칙이 되며, 마지막에는 반복 가능한 검사로 확인됩니다.", - archiveCta: "설계 예시 보기", + archiveCta: "만든 사이트 보기", }, process: { h2: "일곱 단계로 판단과 구현을 연결합니다", @@ -177,18 +177,18 @@ export const landingKo = { facts: [ { value: "7", - label: "설계 단계", - description: "브리프부터 결정 기록까지 0부터 6까지의 단계를 순서대로 수행합니다.", + label: "파이프라인", + description: "조사, 맥락 판단, 구현, 검증과 결정 기록을 0부터 6까지의 단계로 연결합니다.", }, { - value: "15", - label: "하드 게이트", - description: "오버플로, 대비, 토큰, SEO와 죽은 코드를 완료 전에 확인합니다.", + value: "근거 · 맥락", + label: "기준을 구분하는 판단", + description: "보편적인 사용성 기준과 프로젝트에 맞는 스타일 결정을 분리해 적용합니다.", }, { - value: "KO / EN", - label: "동등한 콘텐츠 구조", - description: "한국어와 영어가 같은 정보 구조와 주요 행동을 공유합니다.", + value: "실화면", + label: "두 종류의 검토", + description: "기능 검사는 자동화하고, 화면에서 읽히는 구성과 위계는 별도로 검토합니다.", }, ], details: { @@ -198,9 +198,9 @@ export const landingKo = { "제목, 본문과 버튼의 간격 및 정렬이 토큰 체계를 따르는지 확인합니다.", "텍스트와 인터페이스 색의 대비를 측정합니다.", "키보드만으로 내비게이션과 주요 행동을 완료할 수 있는지 확인합니다.", - "이미지가 목적에 맞게 contain 또는 cover로 표시되는지 확인합니다.", - "메타데이터, 언어, 대체 텍스트와 ARIA 라벨이 실제 화면과 일치하는지 확인합니다.", - "죽은 선택자, 정의되지 않은 토큰과 불필요한 스크립트를 찾습니다.", + "감소 모션과 200% 확대에서도 내용과 주요 행동이 유지되는지 확인합니다.", + "실제 전후 화면에서 구성, 위계와 반응형 동작을 함께 검토합니다.", + "출처, 메타데이터, 언어, 대체 텍스트와 ARIA 라벨을 화면과 연결하고 토큰·SEO·불필요한 코드를 점검합니다.", ], }, }, @@ -243,7 +243,7 @@ export const landingEn = { hero: { h1: "Design rigor\nfor AI coding", lead: - "Research references, choose a direction, define tokens, build responsively, and verify the result through one repeatable process.", + "Research web design principles and real examples to connect a brand-fit direction with responsive implementation and rendered-screen verification.", command: "npx designpaca", primaryCta: "Copy install command", secondaryCta: "See the deliverables", @@ -251,7 +251,7 @@ export const landingEn = { outputs: { h2: "Decisions carry through to code and checks", lead: "Principles selected from research are recorded, translated into design tokens and implementation rules, then verified through repeatable checks.", - archiveCta: "Browse design examples", + archiveCta: "Explore example sites", }, process: { h2: "Connect judgment to implementation in seven stages", @@ -300,18 +300,18 @@ export const landingEn = { facts: [ { value: "7", - label: "Design stages", - description: "Move in order from stage 0, the brief, through stage 6, the decision record.", + label: "Pipeline stages", + description: "Connect research, contextual judgment, implementation, verification, and decision records from stage 0 through stage 6.", }, { - value: "15", - label: "Hard gates", - description: "Check overflow, contrast, tokens, SEO, and dead code before declaring the work complete.", + value: "Evidence · context", + label: "Criteria kept distinct", + description: "Apply universal usability criteria separately from style decisions that fit the project.", }, { - value: "KO / EN", - label: "Equivalent content structure", - description: "Korean and English share the same information architecture and primary actions.", + value: "Rendered screens", + label: "Two kinds of review", + description: "Automate functional checks, then separately review the composition and hierarchy users actually see.", }, ], details: { @@ -321,9 +321,9 @@ export const landingEn = { "Check that heading, body, and button spacing follows the token system.", "Measure contrast for text and interface controls.", "Complete navigation and primary actions with a keyboard.", - "Confirm each image uses contain or cover for its purpose.", - "Match metadata, language, alt text, and ARIA labels to the rendered page.", - "Flag dead selectors, undefined tokens, and unnecessary scripts.", + "Keep content and primary actions available with reduced motion and at 200% zoom.", + "Review real before-and-after screens for composition, hierarchy, and responsive behavior.", + "Connect sources, metadata, language, alt text, and ARIA labels to the rendered page, then check tokens, SEO, and unnecessary code.", ], }, }, diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 6fd7160..db65b21 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,11 @@ # designpaca +## 0.11.0 + +### Minor Changes + +- 40개 근거의 종류·적용 범위·한계를 분리한 ledger와 불확실성 라우팅, 구도·타입·이미지·스타일 가이드, 규범 적합성과 취향·과업 평가의 구분, 적용 가능한 게이트 계약을 번들에 반영했다. + ## 0.10.0 ### Minor Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 806c31f..89bfeb3 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "designpaca", - "version": "0.10.0", + "version": "0.11.0", "description": "웹 디자인 파이프라인 스킬 — Claude Code · Codex · Cursor 에 한 줄로 설치한다", "keywords": [ "design", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index cf89534..4110fec 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,11 @@ # @designpaca/core +## 0.11.0 + +### Minor Changes + +- 40개 근거의 종류·적용 범위·한계를 분리한 ledger와 불확실성 라우팅, 구도·타입·이미지·스타일 가이드, 규범 적합성과 취향·과업 평가의 구분, 적용 가능한 게이트 계약을 동기화했다. + ## 0.10.0 ## 0.9.2 diff --git a/packages/core/package.json b/packages/core/package.json index 4f5e78f..eea8903 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@designpaca/core", - "version": "0.10.0", + "version": "0.11.0", "private": true, "description": "designpaca 설치 엔진 — 타깃 어댑터, 매니페스트, 드리프트 감지", "type": "module", diff --git a/packages/skill/CHANGELOG.md b/packages/skill/CHANGELOG.md index 0bcdfcb..c294093 100644 --- a/packages/skill/CHANGELOG.md +++ b/packages/skill/CHANGELOG.md @@ -1,5 +1,15 @@ # CHANGELOG +## 0.11.0 + +### Minor Changes + +- 40개 근거의 종류·적용 범위·한계를 분리한 ledger와, 판단이 애매할 때 불확실성에 맞춰 참조 문서를 여는 라우팅을 추가했다. 구도·타입·이미지·스타일 가이드, 규범 적합성과 취향·과업 평가의 구분, 적용 가능한 게이트 계약도 정비했다. + +- 초광폭을 별도 레이아웃 상태로 취급하도록 1920·2560px 실렌더 매트릭스, 읽는 `--measure`와 넓은 정보 표면의 분리, 컨트롤·입력·표 셀의 실제 인셋/겹침 검사를 추가했다. 8 CSS px 인셋은 WCAG 수치가 아니라 프로젝트 품질 기본값으로 명시했다. +- CSS Color 4 `color(srgb …)`·알파 합성과 sr-only/aria-hidden/overlay의 가시 텍스트 판정을 감사 하니스 규칙으로 승격했다. UI가 아니라 측정기가 틀린 경우도 RED fixture에서 먼저 재현하도록 한다. +- 근거는 W3C WCAG, W3C Tables Tutorial, Apple HIG Layout/Text Fields, Nielsen Norman Group의 폼 여백 연구를 반영했다. + ## 0.10.0 ### Minor Changes diff --git a/packages/skill/SKILL.md b/packages/skill/SKILL.md index 3496671..45558ee 100644 --- a/packages/skill/SKILL.md +++ b/packages/skill/SKILL.md @@ -33,14 +33,14 @@ description: "웹 디자인 전 과정을 끌고 가는 파이프라인 스킬. 1. **레퍼런스 없이 시작하지 않는다.** 전체 경로에서 1단계를 건너뛴 디자인은 5단계에서 실패 처리한다. 연장·국소 경로는 `design.md` 가 그 자리를 대신한다 — 그것도 없이 건너뛰면 실패다. 2. **모든 시각적 결정에는 브리프로 소급되는 이유가 있어야 한다.** "보통 이렇게 한다"는 이유가 아니다. -3. **화려함은 4순위다.** Awwwards 배점이 Design 40 / Usability 30 / Creativity 20 / Content 10이다. 3D를 얹는 것보다 타입 스케일을 정돈하는 게 결과에 두 배 유효하다. -4. **대담함은 한 곳에만.** 리스크는 하나다. 나머지는 조용히 받쳐준다. 두 곳에서 소리치면 둘 다 죽는다. +3. **화려함만으로 통과하지 않는다.** Awwwards의 공개 평가 비중은 Design 40 / Usability 30 / Creativity 20 / Content 10이다. 이 비중은 해당 어워드의 심사 틀일 뿐, 제품 성과나 특정 기법의 효과를 보장하지 않는다. 출처와 적용 조건은 `references/evidence-ledger.md`에 남긴다. +4. **대담함은 의도와 검증을 같이 가진다.** 강한 선택을 채택하면 어떤 사용 과업·브랜드 목표를 돕는지, 어느 화면에서 어떻게 확인할지 적는다. 리스크의 개수는 보편 규칙이 아니라 브리프·표면 밀도·렌더 결과로 판단한다. 5. **성능 예산을 넘기면 그 이펙트는 채택하지 않는다.** 예산은 3단계에서 정하고 5단계에서 검증한다. 데모·포트폴리오 브리프면 예산을 올려도 되지만 **올렸다고 말해야 한다**. -6. **완료의 정의는 게이트 통과다(자율 검증 폐쇄 루프).** 5단계에서 `tools/design-gate.mjs` 를 프로젝트에 심어 돌리고, 실패 항목이 있으면 4단계로 돌아가 고치고 **전체를 재실행**한다. 게이트 없이 6단계로 넘어가지 않는다. 사용자를 QA 로 쓰지 않는다 — 검증은 기본 내장, 사용자 피드백은 보조다. 상세는 `references/audit-gate.md`. **게이트는 계측이고 계측엔 공백이 있다**(왼쪽 인셋·시각적 무게는 오버플로 검사를 통과한다). 사용자 보고가 오면 방어하지 말라 — 스크린샷을 픽셀 판정으로 해부해 원인을 수치화하고, 그 검사를 게이트에 추가해 재발을 막는다(하니스 규칙 15~17). +6. **완료는 기능·프로젝트 계약·렌더 시각 평가가 함께 확인된 상태다(자율 검증 폐쇄 루프).** 5단계에서 변경 영향 범위에 맞는 자동 검사와 실제 렌더 평가를 수행한다. 실패 항목이 있으면 4단계로 돌아가 고치고, 고친 영역과 그에 연결된 회귀 위험을 다시 검사한다. 사용자를 QA 로 쓰지 않는다 — 검증은 기본 내장, 사용자 피드백은 보조다. 상세는 `references/audit-gate.md`. **게이트는 계측이고 계측엔 공백이 있다**(왼쪽 인셋·시각적 무게는 오버플로 검사를 통과한다). 사용자 보고가 오면 방어하지 말라 — 스크린샷을 픽셀 판정으로 해부해 원인을 수치화하고, 그 검사를 게이트에 추가해 재발을 막는다(하니스 규칙 15~17). 7. **OS·플랫폼 관습은 조사 없이 바꾸지 않는다.** 내비 구조·뒤로가기·시트·safe area·터치 타깃 같은 관습은 사용자가 이미 OS 전체에서 배운 것이다. "웹에서는 그랬다"를 폰에 옮기면 백 키가 앱을 종료하는 웹앱이 된다(실측 사고). 바꾸기 전에 HIG·M3·NN/g 에서 근거를 찾고, 모바일 앱 수준 브리프면 `references/mobile-app-ux.md` 를 구현 전에 읽는다. 8. **언어와 문화권은 컴퓨터 로케일을 기본값으로 삼는다.** 새로 쓰는 UI 카피·메타데이터·빈 상태·오류 문구·장식 텍스트·이미지 안의 글자는 OS 로케일의 언어로 만든다. 영어는 기술 용어·국제 브랜드·짧은 보조 표기처럼 필요한 범위에서만 허용한다. 그 밖의 언어·문자(한국어 로케일에서는 한자·중국어·일본어 포함)와 이를 흉내 낸 인장·세로쓰기·서예·동아시아 이국화 장식은 사용자가 명시적으로 요청하거나 원본 브랜드 자산으로 제공하지 않는 한 만들지 않는다. 원본에 있는 고유명사·인용문은 임의로 고치지 않되, 에이전트가 새 레이블로 증식시키지 않는다. -**접근성은 예외다. 이것만은 협상하지 않는다.** 키보드·포커스·대비·`prefers-reduced-motion`은 어떤 이펙트보다, 어떤 브리프보다 우선한다. 픽셀은 왜곡해도 DOM은 살린다. +**판정 위계를 섞지 않는다.** (1) WCAG·키보드·포커스·대비·감소 모션·진실성처럼 규범 또는 기능에 근거한 항목은 하드 게이트다. (2) 프로젝트의 브리프·`design.md`·토큰·성능 예산은 그 프로젝트의 계약이다. (3) 색·radius·정렬·레이아웃 반복·마퀴·제목 줄 수 같은 스타일 휴리스틱은 관찰 후보일 뿐이다. 후보는 맥락 적합성과 실제 렌더를 보고 채택·수정·유지한다. 픽셀은 왜곡해도 DOM은 살린다. ## 파이프라인 @@ -48,11 +48,25 @@ description: "웹 디자인 전 과정을 끌고 가는 파이프라인 스킬. 참조 문서는 **해당 단계에 들어갈 때 읽는다.** 처음부터 전부 읽지 마라 — 컨텍스트 낭비다. +### 불확실성 라우팅 + +결정이 애매할 때는 감으로 규칙을 더 만들지 말고, 아래에서 **필요한 문서만** 연다. 읽은 근거는 프로젝트의 `design.md` 또는 evidence 기록에 `질문 → 근거/적용 범위 → 결정 → 렌더·과업 검증`으로 남긴다. + +| 판단 질문 | 읽을 문서 | 꺼내 쓸 항목 / 남길 결정 | +|---|---|---| +| 무엇을 먼저 보이게 하고, 어떤 구도·이미지·크롭이 과업을 돕는가? | `references/art-direction.md` | 콘텐츠 우선순위, 시선·여백·이미지 역할, 크롭 비교와 선택 이유 | +| 이 원칙이 보편 규범인가, 특정 사례·실무 지침인가? | `references/design-foundations.md` + `references/evidence-ledger.md` | 주장 종류(원론·규범·실무·사례·전망·서지), 출처의 조건과 적용 여부 | +| 이 브리프에 맞는 시각 언어와 피해야 할 관습은 무엇인가? | `references/style-playbook.md` | 스타일의 표현 수단·주의점, 브리프에 맞춘 변형 | +| 실제 언어·글꼴·숫자에서 읽히는가? | `references/typography.md` | 실제 카피 proof sheet, 폰트·폭·행간·폴백 결정 | +| 무엇을 어떤 범위까지 검사하고 완료라 할 수 있는가? | `references/preflight.md` + `references/audit-gate.md` | 기능·접근성 검사, 같은 조건의 렌더 평가, 영향 범위 | + +자료가 부족하거나 오래됐거나 출처끼리 충돌하면, 권위 있는 원문·공식 규범·해당 분야의 사례를 필요한 범위에서 추가 조사한다. 출처를 많이 모으는 대신 **주장, 적용 범위, 반대 근거, 검증 방법**을 기록한다. 출처와 프로젝트 계약이 충돌하면 기존 우선순위(`사용자 지시 → design.md → 기존 토큰·코드 → 기본값`)를 적용하고, 규범·기능 요구의 적용 수준과 예외를 확인한다. 기능·접근성 요구와 계약이 충돌하면 양쪽을 충족할 대안을 먼저 찾고, 해소되지 않으면 충돌을 명시한다. 취향 휴리스틱으로 필수 요구를 덮지 않는다. 이 과정은 현재 프로젝트의 결정에 쓰는 것이며, 설치된 스킬 자체를 매번 고치는 지시는 아니다. + --- ### 0단계 — 브리프 게이트 -**먼저 프로젝트 루트에 `design.md` 가 있는지 확인한다.** (프로젝트 루트 = 패키지 매니저 설정 파일이 있는 디렉터리. 모노레포면 **작업 대상 패키지의 루트**를 쓰고, 저장소 루트에도 있으면 둘 다 읽되 가까운 쪽이 이긴다) 있으면 읽고, 그 결정을 이 스킬의 기본값보다 우선한다. 같은 프로젝트를 두 번째로 작업할 때 지난번과 다른 디자인이 나오면 그건 실패다. +**먼저 프로젝트 루트에 `design.md` 가 있는지 확인한다.** (프로젝트 루트 = 패키지 매니저 설정 파일이 있는 디렉터리. 모노레포면 **작업 대상 패키지의 루트**를 쓰고, 저장소 루트에도 있으면 둘 다 읽되 가까운 쪽이 이긴다) 있으면 읽고, 그 결정을 이 스킬의 기본값보다 우선한다. 같은 프로젝트는 기존 계약과 사용자의 기대를 유지한다. 다만 사용자가 명시적으로 리디자인을 요청했거나 감사 결과가 방향 변경을 뒷받침하면, 바꾸는 이유·보존할 자산·검증 조건을 기록하고 새 방향으로 진행한다. 브리프를 세 줄로 압축한다. 세 줄을 못 쓰면 아직 작업을 시작할 수 없다. @@ -129,46 +143,35 @@ description: "웹 디자인 전 과정을 끌고 가는 파이프라인 스킬. 결과를 크게 바꾸는 축을 골라 **선택지와 함께** 한 화면에 낸다. 각 선택지에는 "이걸 고르면 무엇이 달라지는지"를 적어라. 고르는 사람이 결과를 예상할 수 있어야 한다. -거의 모든 브리프에서 다음이 결과를 가장 크게 바꾼다. **위 넷은 거의 항상 묻는다.** +브리프·기존 코드·브랜드 자산에서 답을 확인할 수 없고 결과를 크게 바꿀 때만 아래 항목을 묻는다. 이미 알려진 사실을 다시 묻지 않는다. | 축 | 무엇이 달라지는가 | 안 물으면 | |---|---|---| | **업종·성격** | 정보 구조 전체. 같은 "꽃집"도 구독형과 하이엔드 스튜디오는 다른 사이트다 | 카테고리 평균이 나온다 | | **목표 행동** | 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` 의 **모바일 내비** 절을 본다. +여는 메뉴가 필요하다고 판단되면 `references/layout.md` 의 **모바일 내비** 절을 본다. 항목이 적어도 긴 번역·깊은 계층이면 여는 메뉴가 맞을 수 있고, 항목이 많아도 우선순위가 뚜렷하면 일부를 분리할 수 있다. 넷을 `multiSelect` 로 낼지 단일 선택으로 낼지 구분해라 — **목표 행동은 대개 복수**고, -톤과 성격은 하나여야 한다. 둘 다 고르면 방향이 서지 않는다. +톤과 성격에는 주된 방향을 두되, 브리프가 요구하면 서로 보완하는 속성을 함께 쓸 수 있다. 어떤 속성이 위계를 이끄는지 기록한다. **물어도 되는 것과 물으면 안 되는 것** @@ -192,15 +195,17 @@ description: "웹 디자인 전 과정을 끌고 가는 파이프라인 스킬. 이 단계가 designpaca의 심장이다. 머릿속 기본값이 아니라 **실제로 존재하는 사이트**에서 시작한다. 연장·국소 경로는 0단계에서 이미 이 단계를 건너뛰기로 정했고, 그 근거는 `design.md` 다. -레퍼런스 **3개**를 서로 **다른 층위**에서 고른다: +전체 경로에서는 `references/design-foundations.md`를 읽어 이론을 브리프의 사용 과업·제약으로 번역하고, `references/evidence-ledger.md`에서 출처 ID와 기록 형식을 확인한다. 조사 결과는 패키지 문서에 쓰지 말고 프로젝트의 `design.md` 또는 프로젝트 evidence 기록에 남긴다. + +레퍼런스 슬롯은 구조·톤·디테일을 **분리해 검토하는 장치**다. 새 화면에는 보통 아래 세 슬롯을 채우되, 세 개를 찾았다는 사실이 좋은 합성이나 결과를 보증하지 않는다. 브리프에 맞지 않는 슬롯은 비워 둔 이유와 대체 근거를 남긴다: | 슬롯 | 무엇 | 규칙 | |---|---|---| | **R1 — 구조** | 같은 업종/목적의 사이트 | 정보 구조와 흐름을 가져온다 | -| **R2 — 톤** | **반드시 다른 업종** | 분위기·재질·타이포 감각을 가져온다 | +| **R2 — 톤** | 다른 업종 또는 매체를 우선 검토 | 분위기·재질·타이포 감각을 가져온다 | | **R3 — 디테일** | 어디서든 | 하나의 구체적 기법(모션·타입·인터랙션) | -R1과 R2를 같은 업종에서 고르면 결과는 그 업종의 평균이 된다. SaaS 구조 + SaaS 톤 = 또 하나의 SaaS 슬롭이다. +R1과 R2의 업종이 같아도 평균을 무비판적으로 복제하지 말고, 가져올 원칙·적용 조건·실제 렌더 검증을 분리해 기록한다. **목표 시장이 다른 레퍼런스는 자동으로 중립이 되지 않는다.** 단위·용어·문의 방식·사진 조도·신뢰 표지는 문화권의 일부다. 시장 적합성이 중요한 브리프는 R1을 같은 목표 시장에서 우선 찾고, R2는 **같은 문화권의 다른 업종**을 우선한다. 다른 문화권의 R3는 구현 기법 하나만 가져온다. 상세 계약은 `reference-method.md`와, 가상·AI·규제 쇼케이스면 `trustworthy-showcases.md`를 따른다. @@ -209,23 +214,24 @@ R1과 R2를 같은 업종에서 고르면 결과는 그 업종의 평균이 된 **갤러리 목록 페이지가 아니라 원본 사이트를 열어라.** 이미지를 볼 수 없어도 구조는 읽을 수 있다. -**403·404·타임아웃을 두 번 만나면 텍스트 페처를 버리고 브라우저를 띄워라.** -좋은 레퍼런스일수록 봇을 막는다. 대체 갤러리를 찾아 헤매는 것은 시간 낭비이고, -찾아낸 대체 소스는 애초에 보려던 것보다 나쁘다. `designpaca tools` 로 설치된 +**403·404·타임아웃을 두 번 만나면 텍스트 페처만 반복하지 말고 브라우저 또는 다른 정본 출처를 검토한다.** +대체 출처는 처음 후보보다 자동으로 열등하지 않다. 출처가 제공하는 관찰 범위·신뢰성·브리프 적합성을 비교해 고른다. `designpaca tools` 로 설치된 headed 브라우저가 있으면 스크린샷과 실측값을 바로 받는다 — 방법은 `galleries.md` §5. -> 통과 조건: R1/R2/R3 각각의 URL과, 그것에서 **무엇을 가져올지** 한 줄씩. 형식은 `reference-method.md` 참조. +> 통과 조건: 사용한 슬롯마다 URL·출처 ID·원칙/조건·관찰·결정·검증 계획이 있다. 형식은 `reference-method.md`와 `evidence-ledger.md`를 따른다. --- ### 2단계 — 방향 결정 -레퍼런스 3개를 하나의 방향으로 합성한다. 베끼지 않는다. **축을 정하고 그 축 위에서 결정한다.** +사용한 레퍼런스 슬롯을 하나의 방향으로 번역한다. 베끼지 않는다. **축을 정하고 그 축 위에서 결정한다.** + +이 단계에서 `references/style-playbook.md`를 읽어 선택한 스타일의 표현 수단·주의점·적용 조건을 검토한다. 이론이나 스타일 이름은 결론이 아니므로, `design.md` 또는 프로젝트 evidence 기록에 출처 ID → 원칙/조건 → 관찰 → 결정 → 검증 계획을 남긴다. 정해야 할 것: - **한 문장 컨셉** — 이 사이트가 주는 인상을 한 문장으로. ("고급 잡지의 여백", "계기판처럼 정확한", "밤의 스튜디오") - **미학 프리셋** — 아래에서 고르거나, 브리프가 요구하면 새로 정의한다 -- **감수할 리스크 하나** — 정당화할 수 있는 과감한 선택 하나. 없으면 그 디자인은 안전하고 잊힌다 +- **표현 리스크** — 과감한 선택을 쓴다면 왜 이 과업에 맞는지와 실제 렌더 검증 방법. 리스크가 없다는 결정도 허용하되 이유를 기록한다 - **표면 다이얼** — 랜딩·관리자·가맹처럼 과업이 다른 화면이 함께 있으면 각 표면의 표현성·정보 밀도·모션·증거 노출을 0~3으로 따로 적는다. 공통 토큰은 공유하되 과업 밀도까지 같게 만들지 않는다 | 프리셋 | 한 줄 | 언제 | @@ -238,7 +244,7 @@ headed 브라우저가 있으면 스크린샷과 실측값을 바로 받는다 고른 프리셋 파일 **하나만** 읽어라. 다섯 다 읽지 마라. 고르는 기준은 `references/presets/README.md`. -> 통과 조건: 한 문장 컨셉 + 프리셋 + 리스크 하나가 적혔다. +> 통과 조건: 한 문장 컨셉 + 프리셋 + 표현 리스크의 채택/비채택 근거가 적혔다. > **연장 경로는 이 단계를 돌지 않는다.** `design.md` 의 컨셉·프리셋·리스크를 그대로 이어받는다. 바꾸고 싶으면 전체 경로로 올린다. --- @@ -253,7 +259,7 @@ headed 브라우저가 있으면 스크린샷과 실측값을 바로 받는다 **성능 예산도 여기서 정한다.** 나중에 정하면 이미 늦는다. 전체 표는 `references/tokens.md` §5 하나뿐이다 — 다른 문서에 예산 표를 만들지 마라. -기본선: 히어로까지 JS **150KB(gzip)** / 첫 인터랙션 **3초** / 애니메이션은 `transform`·`opacity` 만. +기본선: 히어로까지 JS **150KB(gzip)** / 첫 인터랙션 **3초**. `transform`·`opacity`는 저비용 기본 선택이다. 다른 속성 또는 스크롤 기반 로직은 자동 금지가 아니라, 측정한 비용·입력 간섭·감소 모션 경로·프로젝트 예산을 기록하고 실제 기기/브라우저에서 검증한다. 브리프가 데모·포트폴리오라면 예산을 올려도 된다. **올린다는 사실과 이유를 명시해라.** > 통과 조건: 토큰이 실제 값으로 적혔고, 성능 예산이 숫자로 정해졌다. @@ -266,9 +272,9 @@ headed 브라우저가 있으면 스크린샷과 실측값을 바로 받는다 **4-0. 이미지 조달** → `references/images.md` 자리를 만들기 전에 **무엇을 실을지** 정한다. 나중에 채우면 비율이 안 맞아 레이아웃을 다시 짠다. -사용자가 준 사진이 있으면 무조건 그것이고, 없으면 **codex 가 설치돼 있는지 확인해 생성**한다. -생성했으면 `design.md` 에 생성물이라고 적는다 — 나중에 실제 사진으로 바꿀 사람이 알아야 한다. -히어로·증거 컷·모바일 히어로를 각각 슬롯으로 적은 **이미지 바이블**을 먼저 만든다. 데스크톱 크롭에서 핵심 피사체가 폴드 아래로 밀리면 모바일은 별도 원본을 만든다. +사용자가 준 사진·기존 브랜드 자산·직접 제작·라이선스 가능한 자료·생성물 중에서, 이미지 슬롯의 역할과 품질·권리·진실성에 맞는 것을 고른다. 자산이 없다는 사실만으로 자동 생성하지 않는다. +생성물을 쓰면 `design.md` 에 생성물과 용도를 적는다 — 나중에 실제 사진으로 바꿀 사람이 알아야 한다. +히어로·증거 컷·모바일 히어로를 각각 슬롯으로 적은 **이미지 바이블**을 먼저 만든다. 슬롯별 실제 크롭에서 핵심 피사체와 맥락이 보존되는지 확인하고, 같은 원본의 초점 조정으로 해결되지 않을 때만 별도 자산을 고른다. 싣기 전에 반드시 줄인다(실측: 10.1MB → 603KB). **4-1. 레이아웃과 타이포그래피** → `references/layout.md` (+ 폰트 적용은 `references/typography.md` §5, §6) @@ -285,7 +291,7 @@ headed 브라우저가 있으면 스크린샷과 실측값을 바로 받는다 **4-5. 서류로서의 완성** — 프리셋이 서류·원장·콘솔 계열(장부, 시간표, 관리 화면)일 때만 도는 단계다. 그 앱이 종이에서 하던 일을 화면이 이어받아야 프로덕션이다. 셋을 검토한다: -- **인쇄** — `@media print`. 앱 크롬(내비·툴바·버튼)은 물러나고 활성 화면이 서류가 된다. 모달이 열려 있으면 그 서류만(`body:has(dialog[open])` 로 앱을 숨긴다). 상태색은 `print-color-adjust: exact` 로 보존. 인쇄 버튼을 크롬에 더했다면 **그 줄의 최소폭 합**을 가장 좁은 폭에서 다시 잰다(하드 게이트 5) +- **인쇄** — `@media print`. 앱 크롬(내비·툴바·버튼)은 물러나고 활성 화면이 서류가 된다. 모달이 열려 있으면 그 서류만(`body:has(dialog[open])` 로 앱을 숨긴다). 상태색은 `print-color-adjust: exact` 로 보존. 인쇄 버튼을 크롬에 더했다면 프로젝트의 최소 지원 폭에서 실제 조작 가능성을 다시 잰다 - **키보드 단축키** — 콘솔의 손가락 문법. 숫자키 뷰 전환, `/` 검색 포커스. `kbd` 물리 키 칩으로 안내하고, 입력 요소에 포커스가 있을 때는 무력화한다 - **본연의 도메인 동작** — 학적부는 기록, 시간표는 격자, 주문은 원장. 그 도메인이 종이에서 하던 핵심 동작 하나가 빠져 있으면 그게 곧 '데모 티'다 @@ -306,54 +312,52 @@ HTML-in-Canvas(`drawElementImage`)는 **폴백을 완성한 뒤에만** 얹는 → `references/preflight.md` (전체 체크리스트) → `references/antipatterns.md` (슬롭 지문 목록 + grep 검출 + 자가 채점표) -특히 다음 다섯은 기계적으로 검사할 수 있다. **반드시 돌려라**: +다음 항목을 변경 영향과 프로젝트 검증 계약에 맞춰 확인한다. 자동 검사와 렌더 평가는 구분한다: -1. **슬롭 지문 grep** — 보라 CTA(`#6366f1`·`#8b5cf6` 계열), 전체 대문자 헤드라인, 번호 매긴 1·2·3 단계. 실측 검출률 상위 항목이다 +1. **스타일 후보 grep** — 보라 CTA, 전체 대문자 헤드라인, 번호 매긴 단계처럼 반복되는 기본값을 찾아 근거 없이 남아 있는지 검토한다. 검출 자체는 실패가 아니며 `antipatterns.md`의 맥락 질문과 실제 렌더로 판정한다 2. **카피 경쟁사 치환 테스트** — 제품명을 경쟁사 이름으로 바꿔도 문장이 성립하면, 그 카피는 아무것도 말하지 않았다 3. **이펙트 전부 끄기** — CSS 필터·WebGL·애니메이션을 끈 상태에서 페이지가 여전히 읽히는가 -4. **게이트 실행(폐쇄 루프)** — `tools/design-gate.mjs --init` 으로 설정을 만들고 돌린다. 오버플로·h1·수축·대비·SEO/meta·죽은 선택자·토큰 위생·스케일×폭까지 전 뷰 × 전 폭. L1(단위)·L5(탐색)·L6(시나리오 E2E)는 프로젝트 `tools/` 에 보강해 `npm run verify` 체인으로. **게이트 통과 없이 완료 선언 금지.** +4. **게이트 실행(폐쇄 루프)** — `tools/design-gate.mjs --init` 으로 설정을 만들고, 변경 영향과 프로젝트 계약에 맞춘 뷰·폭·상태에서 실행한다. 오버플로·제목 존재/가시성·수축·대비·SEO/meta·죽은 선택자·토큰 위생·스케일×폭을 해당 범위에서 검사한다. 명시한 양의 `h1MaxLines` 계약이 있을 때만 줄 수를 검사한다. L1(단위)·L5(탐색)·L6(시나리오 E2E)는 프로젝트에 필요한 경우 `tools/` 에 보강해 `npm run verify` 체인으로. 자동 검사 결과와 실제 렌더 평가를 함께 기록한다. 5. **상태 완결성 스윕** — 입력·파괴·열림이 있는 화면은 실제로 조작해 본다: 수치 입력에 범위 밖 값을 넣고, 파괴적 행동을 되돌려보고, 열린 메뉴를 Esc 로 닫는다. 기준은 `references/preflight.md` §4-1. 통과 못 하면 4단계로 6. **상태별 시각 E2E** — URL별 390·1440 기본 화면과 결과·오류·모달 같은 핵심 상태를 캡처한다. 하니스는 개수·규격·0바이트·중복 해시를 단언하고, 캡처를 실제로 열어 목표 시장 적합성·시선 위계·이미지 크롭·반복을 눈으로 판정한다. 구조 PASS를 시각 PASS로 바꾸어 말하지 않는다 > 게이트가 잡은 실제 사고 목록과 7계층 설계 근거는 `references/audit-gate.md` 다. 자기 검사 스크립트를 매번 새로 쓰지 마라 — 이 게이트를 심고 확장해라. -#### 하드 게이트 — 전부 "아니오"여야 한다 +#### 규범·기능 하드 게이트 — 전부 충족해야 한다 -취향이 아니라 **버그**다. 여기엔 오버라이드가 없다. 체크박스가 아니라 질문이니 실제로 검사해라. +아래는 취향이 아니라 적용 WCAG·플랫폼 접근성·사용자에게 약속한 기능의 실패다. 프로젝트 계약과 스타일 판단은 다음 절에서 별도 검토한다. -1. 토큰 밖에 인라인 hex/rgb/oklch 색상값이나 인라인 `font-family` 가 있는가? -2. 상태색(성공·경고·오류)을 제외하고, 한 페이지에서 강조색이 2개 이상인가? -3. `border-radius` 값이 토큰 밖에 있거나, 서로 다른 값이 3종 이상인가? -4. 페이지 중간에 테마(라이트/다크)가 뒤집히는데, 그 반전이 3단계 토큰에 규칙으로 정의돼 있지 않은가? - (리듬으로 의도한 반전은 통과다. 정의 없이 섹션마다 다른 것이 실패다) -5. 320~1920px 사이 어느 폭에서든 가로 스크롤이 생기는가? -6. 버튼 라벨·내비 링크가 2줄로 접히는 폭이 있는가? -7. 버튼 텍스트와 배경의 대비가 4.5:1 미만인가? -8. `transform`/`opacity` 외의 속성을 애니메이션하는가? -9. 풀하이트 섹션에 `100vh` 를 썼는가? (모바일 주소창 때문에 `100dvh` 여야 한다) -10. `window.addEventListener('scroll')` 을 썼는가? (`IntersectionObserver` 또는 scroll-driven animation 을 써라) -11. **사용자가 주지 않은 수치**(지표·통계·후기·고객 수)가 페이지에 **그럴듯한 값으로** 들어가 있는가? +1. 키보드만으로 모든 인터랙티브 요소에 도달하고, 보이는 포커스와 논리적 순서를 유지하는가? +2. 텍스트·UI·상태가 적용 WCAG 대비 기준을 충족하고, 의미 있는 이미지·폼·제목 구조·언어가 접근 가능한가? +3. 감소 모션 환경에서 움직임 민감도를 낮추면서 콘텐츠·상태·조작을 보존하는가? +4. 지원 범위의 뷰포트·글자 확대·입력 방식에서 의도하지 않은 가로 스크롤, 겹침, 조작 불능이 없는가? +5. 입력·파괴·열림 상태가 실제로 검증되고, 오류·되돌림·Esc·포커스 복귀가 필요한 곳에서 작동하는가? +6. 애니메이션 또는 스크롤 로직이 입력을 방해하지 않고, 정한 성능 예산과 지원 범위를 실제로 검증했는가? +7. **사용자가 주지 않은 수치**(지표·통계·후기·고객 수)가 페이지에 **그럴듯한 값으로** 들어가 있지 않은가? 걸렸으면 셋 중 하나다: (a) `{{SETUP_TIME}}` 같은 **명시적 placeholder** 로 바꾸고 6단계 `design.md` 미확정 목록에 올린다, (b) 사용자에게 실제 값을 묻고 멈춘다, (c) 그 섹션 자체를 다른 구조로 바꾼다. **placeholder 는 통과다.** 숫자 모양의 구멍은 정직하고, 지어낸 숫자는 슬롭이다. -12. `
` 로 만든 가짜 스크린샷·가짜 브라우저바·가짜 폰 프레임이 있는가? -13. **토큰 체계·프리셋을 바꿨는데 브랜드 표면(theme-color 메타·파비콘·og:image)이 옛 값을 그대로 쓰고 있는가?** 실측 사고: 프리셋 전환 11버전 뒤에 종이 시대 파비콘·theme-color 가 살아 있었다. 토큰 마이그레이션은 이 셋을 갱신하기 전까지 끝난 게 아니다. `theme-color` 는 실제 배경색과 같아야 한다(게이트가 비교한다) -14. **HTML·JS 에서 사라진 컴포넌트의 CSS 규칙·JS 함수가 남아 있는가?** 제거는 HTML·CSS·JS 삼위일체다. 게이트의 죽은 선택자 검사가 클래스 사용률을 대조한다 -15. **현재 로케일 기본값·명시 브리프와 무관한 제3언어 또는 문화권 클리셰를 에이전트가 새로 넣었는가?** 화면·메타·ARIA·예제 데이터·생성 이미지와 세로쓰기·인장·서예성 장식까지 실제 렌더에서 확인한다. 한국어 로케일에서는 한자·중국어·일본어의 신규 사용이 이에 해당한다. 영어와 사용자가 제공한 원문은 예외다. +8. 실제 증거로 오인될 수 있는 가짜 스크린샷·브라우저바·폰 프레임을 라벨 없이 만들지 않았는가? -#### 카운트 규칙 +#### 프로젝트 계약 — `design.md`와 구현에서 대조한다 -세어봐라. 넘으면 고친다. +- 토큰·테마 반전·브랜드 표면(theme-color·파비콘·og:image)·남은 코드가 프로젝트 결정을 반영하는가? +- 로케일·문화권·실제 수치·진실 라벨이 브리프와 제공 자산의 범위를 지키는가? +- 지원 viewport·성능 예산·상태별 시각 E2E는 프로젝트가 정한 매트릭스와 도구 한계 안에서 보고됐는가? 자동 도구가 측정하지 못한 항목은 통과로 추정하지 말고 수동 평가로 남긴다. + +#### 스타일 휴리스틱 — 계수는 시작점이지 판정이 아니다 + +세어보고, 반복이 정보·브랜드·사용성에 맞는지 실제 렌더에서 평가한다. 아래 수치를 넘겼다는 사실만으로 고치거나 통과시키지 않는다. - 작은 대문자 라벨(eyebrow) 개수 ≤ `ceil(섹션수 / 3)` - 같은 이미지+텍스트 스플릿 레이아웃 연속 ≤ 2회 -- 마퀴 ≤ 1개 +- 마퀴: 반복·자동재생·정지 수단·감소 모션 경로를 검토한다 - 섹션이 6개 이상이면 서로 다른 레이아웃 패밀리가 최소 3개 -- 히어로: 헤드라인 ≤ 2줄, 서브텍스트 ≤ 20단어, 텍스트 요소 ≤ 4개 +- 히어로: 제목 줄 수·서브텍스트 길이·텍스트 밀도가 목표 행동과 읽기 폭에 맞는지 확인한다. 줄 수를 맞추려고 글자 크기를 줄이지 않는다 **국소 경로는 카운트를 페이지 전체로 다시 세지 않는다.** 내가 손댄 부분이 기존 카운트를 넘기게 만드는지만 본다. -**하드 게이트 15개는 경로와 무관하게 전부 돈다.** +**규범·기능 하드 게이트는 경로와 무관하게 전부 돈다.** -> 통과 조건: 하드 게이트 15개 전부 "아니오", 카운트 규칙 통과, `preflight.md` 체크리스트 통과. 실패 항목이 있으면 4단계로 돌아간다. +> 통과 조건: 규범·기능 하드 게이트 충족, 프로젝트 계약 대조 완료. 기능·접근성 자동 검사와 시각 평가(위계·브랜드·타입·구도·이미지·리듬)를 별도 보고하고, 스타일 휴리스틱의 보류·채택·수정 근거를 남긴다. 실패 항목이 있으면 4단계로 돌아간다. --- @@ -363,8 +367,9 @@ HTML-in-Canvas(`drawElementImage`)는 **폴백을 완성한 뒤에만** 얹는 ```markdown # design.md -브리프 3줄 / 레퍼런스 R1·R2·R3 URL / 한 문장 컨셉 / 프리셋 / 감수한 리스크 하나 +브리프 3줄 / 레퍼런스 슬롯과 출처 ID / 한 문장 컨셉 / 프리셋 / 표현 리스크의 채택·비채택 근거 토큰 전체(타입·색·간격·모션) / 성능 예산과 실측치 / 채택한 이펙트와 그 폴백 +출처 ID → 원칙·조건 → 관찰 → 결정 → 검증 결과를 프로젝트 evidence 기록에 연결 / 기능·접근성 자동 검사와 시각 평가의 별도 결과 의도적으로 하지 않은 것과 그 이유 ``` @@ -382,6 +387,9 @@ HTML-in-Canvas(`drawElementImage`)는 **폴백을 완성한 뒤에만** 얹는 |---|---| | `references/galleries.md` | 1단계 — 어느 갤러리를 볼지 정할 때 / **1′ — 국소 브리프 표** | | `references/reference-method.md` | 1단계 — 레퍼런스를 뜯어볼 때 | +| `references/design-foundations.md` | **1·2단계 — 이론을 프로젝트 조건으로 번역할 때** | +| `references/style-playbook.md` | **2·4단계 — 선택한 스타일의 표현 수단과 금기를 검토할 때** | +| `references/evidence-ledger.md` | **0·1·2·5·6단계 — 출처와 관찰·결정·검증을 추적할 때** | | `references/presets/README.md` | 2단계 — 미학 방향을 고를 때 (고른 프리셋 하나만 추가로 읽는다) | | `references/tokens.md` | 3단계 — 토큰을 정할 때 | | `references/typography.md` | 3단계 — 폰트를 고를 때 / 4-1 — 적용할 때 | @@ -396,7 +404,7 @@ HTML-in-Canvas(`drawElementImage`)는 **폴백을 완성한 뒤에만** 얹는 | `references/preflight.md` | 5단계 — 감사 | | `references/antipatterns.md` | 3·5단계 — 한글 조판 / 슬롭 검출 | | `references/audit-gate.md` | **5단계 — 게이트(폐쇄 루프) 설계·설치·하니스 규칙** | -| `tools/design-gate.mjs` | **5단계 — 범용 게이트 실행기(SEO/meta·대비·수축·페이지별 h1 최대 줄 계약·시각·WebKit 옵션)** | +| `tools/design-gate.mjs` | **5단계 — 범용 게이트 실행기(SEO/meta·대비·수축·명시한 양의 h1 줄 수 계약·시각·WebKit 옵션)** | ## 작업 중 지켜야 할 것 diff --git a/packages/skill/package.json b/packages/skill/package.json index 6a1c7c6..0263f5b 100644 --- a/packages/skill/package.json +++ b/packages/skill/package.json @@ -1,6 +1,6 @@ { "name": "@designpaca/skill", - "version": "0.10.0", + "version": "0.11.0", "private": true, "description": "designpaca 스킬 원본 — SKILL.md 와 참조 문서", "scripts": { diff --git a/packages/skill/references/antipatterns.md b/packages/skill/references/antipatterns.md index ed1bfab..e8afe94 100644 --- a/packages/skill/references/antipatterns.md +++ b/packages/skill/references/antipatterns.md @@ -1,47 +1,43 @@ -# antipatterns.md — 금지 목록 +# antipatterns.md — 점검 후보 -AI가 만든 티는 **못 만들어서** 나는 게 아니라 **결정을 안 해서** 난다. -보라 그라디언트, Inter, 3열 아이콘 카드, `rounded-2xl shadow-lg`, "Get Started"는 전부 **미결정의 기본값**이다. +AI가 만든 티는 **못 만들어서** 나는 게 아니라 결정을 설명·검증하지 않을 때 난다. +보라 그라디언트, Inter, 3열 아이콘 카드, `rounded-2xl shadow-lg`, "Get Started"는 반복될 수 있는 출발점이다. 존재만으로 미결정이라고 단정하지 않고, 브리프·프로젝트 계약·실제 렌더로 판단한다. -프리플라이트에서 이 문서를 훑고 걸린 항목을 전부 해소한다. **4개 이상 걸리면 폐기하고 다시 시작한다.** +프리플라이트에서 이 문서를 훑어 후보를 찾는다. 검출은 실패가 아니다. 브리프·프로젝트 계약·실제 렌더를 대조해 유지·수정·제거를 결정하고 그 근거를 남긴다. --- -## 0. 최우선 — 측정된 상위 지문 +## 0. 먼저 보는 반복 후보 -Show HN 랜딩 1,590개 실측에서 검출률이 가장 높았던 셋이다. **이 셋부터 확인하라.** +원자료와 재현 가능한 표본이 없는 빈도 수치는 규칙의 근거가 될 수 없다. 아래 항목은 흔한 기본값을 찾는 검색 후보이며, 색·대문자·번호가 존재한다는 사실만으로 실패가 아니다. -| 순위 | 지문 | 검출률 | 왜 문제 | 대신 | -|---|---|---|---|---| -| 1 | **CTA 버튼이 보라·인디고 계열** (`bg-indigo-600`, `#6366f1`, `#8b5cf6`) | **10.7%** | Tailwind 기본 팔레트 = "색을 안 골랐다"는 신호 | 브랜드 색. 없으면 중립 대비가 가장 강한 색(잉크 위 화이트 반전) | -| 2 | **헤드라인·섹션 라벨이 전체 대문자** (`FEATURES`, `HOW IT WORKS`) | **10.5%** | 얻지 않은 권위를 빌리는 장치. 가독성도 낮음 | 라벨을 없애고 헤드라인만으로 섹션을 구분 | -| 3 | **번호 매긴 1·2·3 단계 섹션** | **9.4%** | 실제 순서가 아닌데 순서인 척 | 단계가 진짜 순서일 때만. 그 경우에도 번호보다 화면 캡처 3장이 낫다 | - -전체 표본의 22%가 4개 이상 지문을 가진 "heavy slop"이었다. +| 후보 | 확인할 질문 | +|---|---| +| 보라·인디고 CTA | 브랜드 팔레트·상태 구분·대비·목표 행동을 설명하는가? | +| 전체 대문자 라벨 | 언어·가독성·정보 위계에 맞는가? | +| 번호 매긴 단계 | 사용자가 실제 순서로 수행하는 과업인가? | --- ## 1. 컬러 -> **다크가 슬롭이 아니라 기본값이 슬롭이다.** -> bun.sh는 다크 배경인데도 이 절의 항목을 거의 전부 회피한다 — 순흑이 아닌 `#0D0A0C`, 텍스트 3단계, 보라 대신 마젠타 `#FF2E97`, radius 어휘 3종. -> A-01은 "다크를 쓰지 마라"가 아니라 **"고르지 않은 채로 다크에 떨어지지 마라"**는 뜻이다. 아래 전부 동일하다. +다크·그라디언트·강한 색은 그 자체로 실패가 아니다. 화면 역할, 브랜드, 대비, 실제 기기 렌더에서 이유를 설명할 수 있는지 점검한다. | 지문 | 왜 문제 | 대신 | |---|---|---| -| **다크 모드를 브리프 근거 없이 기본값으로** (`bg-slate-900`, `#0B0B0F`) | 생성 도구의 반사 반응. 단일 시그니처 검출 빈도 1위 | 라이트로 설계하고 다크는 토큰으로 파생. 사용자 82%가 다크를 쓴다는 데이터는 "지원하라"이지 "기본값으로 하라"가 아니다 | -| **보라→파랑 그라디언트 히어로** (`from-purple-600 to-blue-500`, `#6366f1→#a855f7`) | 수십만 튜토리얼의 기본값 | 브랜드 색 1개 + 중립. 그라디언트를 쓸 거면 같은 hue 내 명도 변화 (`#1B4332→#2D6A4F`) | -| **라벤더 퍼플이 어디에나** (`#a78bfa`, `#c4b5fd`) | 텍스트→랜딩 생성기의 출력 지문 | 보라를 쓰지 마라. 꼭 필요하면 채도를 낮추고 온도를 틀어라 (`#6B5B95`) | -| **네온 온 다크** — 시안(`#22d3ee`)·바이올렛이 검정 위에서 발광 | 게이밍·개발자 툴 브리프가 아닌데 나오면 즉시 티가 남 | 저채도 액센트 1개. 발광 대신 명도 차이로 위계 | -| **컬러 글로우** (`box-shadow: 0 0 80px rgba(139,92,246,.5)`) | 광원 논리 없이 깊이를 색으로 위조 | 중립 그림자 2단 (`0 1px 2px rgba(0,0,0,.06)`, `0 8px 24px rgba(0,0,0,.08)`) | -| **히어로 뒤 방사형 그라디언트 오브·헤일로** | 구성을 못 잡았을 때의 회피 수단 | 배경을 비우거나 실제 콘텐츠(제품 스크린샷, 타입)로 채워라 | -| **순백 `#ffffff` / 순흑 `#000000`** 을 배경·텍스트로 | 아무 결정도 하지 않았다는 뜻. 눈부심·번짐 유발 | 오프화이트 `#FAFAF7`, 잉크 `#111014`. 브랜드 hue를 2~4% 섞으면 더 좋다 | -| **Tailwind 기본 토큰 그대로** (`slate-900`, `gray-500`, `emerald-500`) | 누구나 알아본다 | `tailwind.config`의 색을 **교체**한다(확장 아님). `ink-900`, `ember-600` 같은 고유 이름 | +| **다크 모드를 브리프 근거 없이 기본값으로** (`bg-slate-900`, `#0B0B0F`) | 대상 환경·브랜드·콘텐츠에 맞는지 설명하지 못하면 선택이 기본값처럼 보일 수 있다 | 라이트·다크의 역할 토큰, 실제 기기 대비, 사용자 설정·테마 전환 필요성을 비교한다 | +| **보라→파랑 그라디언트 히어로** (`from-purple-600 to-blue-500`, `#6366f1→#a855f7`) | 제품·브랜드·정보 역할과 무관하면 배경 장식만 남을 수 있다 | 단색·동일 hue 변화·다중 hue를 같은 콘텐츠에서 비교하고, 텍스트 대비와 행동 위계를 확인한다 | +| **라벤더 퍼플이 어디에나** (`#a78bfa`, `#c4b5fd`) | 맥락 없는 반복은 기본값처럼 보일 수 있음 | 브랜드·접근성·표면 역할을 설명하고 실제 렌더에서 위계를 확인 | +| **네온 온 다크** — 시안(`#22d3ee`)·바이올렛이 검정 위에서 발광 | 색의 의미·대비·주변광에서의 읽기 비용을 확인해야 한다 | 상태·행동·콘텐츠 역할과 연결하고, 저채도/고채도·명도 대비를 실제 기기에서 비교한다 | +| **컬러 글로우** (`box-shadow: 0 0 80px rgba(139,92,246,.5)`) | 깊이·계층·상태를 글로우만으로 표현하면 의미가 모호해질 수 있다 | 그림자·경계·여백·글로우 후보 중 실제 계층과 성능을 가장 잘 드러내는 것을 선택한다 | +| **히어로 뒤 방사형 그라디언트 오브·헤일로** | 메시지·제품 증거·구도보다 장식이 먼저 읽히는지 점검한다 | 배경·제품 화면·타입 중심 구도를 같은 콘텐츠로 비교한다 | +| **순백 `#ffffff` / 순흑 `#000000`** 을 배경·텍스트로 | 극단값이 브랜드·매체·환경에 맞는지 점검한다 | 대상 기기·주변광·텍스트 대비와 실제 렌더의 눈부심을 확인한다. 색 자체를 실패로 취급하지 않는다. 출처: [WCAG 2.2 Contrast](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html)와 프로젝트 브리프 | +| **Tailwind 기본 토큰 그대로** (`slate-900`, `gray-500`, `emerald-500`) | 역할·브랜드·상태가 토큰 이름과 값에서 드러나는지 점검한다 | 기본 토큰을 유지·확장·교체할 수 있다. 의미 역할과 실제 렌더 근거를 기록한다 | | **코드에서 색 이름을 직접 호출** (`purple-500`) | 색이 역할을 못 가짐 | 의미 토큰: `--color-action-primary`, `--color-surface-elevated`, `--color-text-secondary` | -| **그라디언트 텍스트** (`background-clip: text`) | 스캔성을 깎고 정보는 0. OG 이미지에서 깨짐 | 단색. 강조는 크기·굵기·여백으로 | +| **그라디언트 텍스트** (`background-clip: text`) | 작은 크기·이미지 배경·공유 이미지에서 읽기와 fallback을 점검한다 | 단색·그라디언트·다른 위계 수단을 비교하고, 실제 export와 대비를 확인한다 | | **크림/베이지(`#FDF8F3`)를 "고급"의 기본값으로** | 보라를 대체한 신종 슬롭 | 크림을 쓸 거면 왜 크림인지 브리프와 연결하고 텍스트·액센트를 그에 맞춰 재설계 | -| **hue 4개 이상** | 결정 못 한 상태 | hue 3개 이하. 면적 60(지배)/30(중립)/10(강조) | -| **다크에서 본문 대비 미달** (`#0f172a` 위 `#94a3b8`) | 기능적 결함 | 본문 4.5:1 또는 APCA Lc ≥ 75. 다크의 본문은 순백 대신 `#E8E6E3` | +| **여러 hue가 경쟁함** | 행동·상태·정보의 우선순위가 색 때문에 흐려질 수 있다 | 색 역할과 화면 면적을 실제 상태에서 비교한다. hue 수·60/30/10은 출발 가설일 뿐 통과 기준이 아니다 | +| **다크에서 본문 대비 미달** | 적용 WCAG 성공 기준을 실제 배경 합성값으로 충족하는지 점검한다 | `#0f172a` 위 `#94a3b8`의 WCAG 대비는 약 **6.96:1**이므로 미달 사례가 아니다. WCAG 2.2 AA 본문 판정에는 contrast ratio를 사용하며 APCA는 이를 대체하지 않는다. 반투명·이미지 배경은 실제 렌더에서 계산한다. 출처: [WCAG 2.2 Contrast](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html) | --- @@ -49,18 +45,17 @@ Show HN 랜딩 1,590개 실측에서 검출률이 가장 높았던 셋이다. ** | 지문 | 왜 문제 | 대신 | |---|---|---| -| **Inter를 이유 없이 기본값으로** (특히 중앙 정렬 히어로) | AI 인터페이스의 기본 서체 | Satoshi / Switzer / DM Sans / Work Sans. Inter는 초고밀도 다국어 UI 같은 명확한 이유가 있을 때만 | -| **Space Grotesk + Instrument Serif + Geist 조합** | 이 셋의 **반복 조합**이 생성 지문으로 특정됨 | 셋 중 **하나만** 쓰는 것은 허용. 둘 이상 함께 쓰면 실패. 파운드리를 바꾸면 더 낫다 (Fontshare, Velvetyne) | -| **헤드라인 한 단어만 세리프 이탤릭** (`Build better products`) | 가장 널리 퍼진 "성의 표시" 관용구. 이제는 성의가 아니라 지문 | 강조는 줄바꿈·크기·색·여백으로 | -| **페이지 전체에 폰트 패밀리 1개** | 위계를 크기로만 만들게 되어 밋밋 | 디스플레이 1 + 본문 1. 성격 차이를 크게 | -| **타입 스케일이 평평함** (16/18/20/24, 비율 1.15 미만) | 모든 게 똑같이 중요해 보임 | 비율 1.25 또는 1.333. **최대/본문 4배 이상** (17→68). 비율만으로 4배가 안 나오면(1.25 5단계 = 2.44배) **디스플레이 사이즈를 스케일 밖에 따로 정의한다**(`tokens.md` §1). **단계 수를 늘려서 맞추지 마라** — 그건 아래 행에 걸린다 | -| **타입 스케일 단계 8개 이상** | 통제 실패 | 3~5단계로 제한하고 각 단계에 역할 이름 부여 | -| **히어로 = pill 배지 + 거대 헤드라인** (`✨ Now in beta` + H1) | 가장 알아보기 쉬운 구조 지문 | 배지를 지워라. 정말 필요하면 문장 안이나 내비 옆으로 | +| **Inter 선택** (특히 중앙 정렬 히어로) | 서체가 언어·정보 밀도·브랜드 톤에 맞는지 점검한다 | 후보 서체와 숫자·한글·긴 제목을 같은 viewport에서 비교하고, 라이선스·fallback metric을 기록한다. Inter 자체를 실패로 취급하지 않는다. 출처: `references/typography.md`와 프로젝트 브리프 | +| **Space Grotesk + Instrument Serif + Geist 조합** | 조합이 실제 위계와 브랜드 톤을 만드는지 점검한다 | 서체 수가 아니라 역할·fallback·가독성을 실제 렌더에서 비교한다. 조합의 구성원 수만으로 실패시키지 않는다. 출처: `references/typography.md` | +| **헤드라인 한 단어만 세리프 이탤릭** (`Build better products`) | 강조가 브랜드 언어·문장 의미·읽기 순서를 실제로 돕는지 점검한다 | 이탤릭·줄바꿈·크기·색·여백을 같은 문구에서 비교하고 선택 이유를 기록한다 | +| **페이지 전체에 폰트 패밀리 1개** | 서체 수와 무관하게 제목·본문·데이터의 위계가 읽히는지 점검한다 | 한 패밀리 또는 여러 패밀리를 같은 콘텐츠·viewport·확대 상태에서 비교하고, 역할과 fallback을 기록한다. 출처: `references/typography.md` | +| **타입 스케일이 평평해 보임** | 최대/본문 배율이 아니라 실제 과업의 위계·읽기 폭·언어 적합성을 점검한다 | 제목·본문·보조 정보의 역할과 렌더된 우선순위를 비교한다. 배율·단계 수는 관찰값으로 기록하되 최대/본문 4배나 특정 비율을 보편 통과 조건으로 쓰지 않는다. 출처: `references/design-foundations.md`, `references/typography.md` | +| **타입 스케일 단계가 많음** | 같은 역할에 여러 크기가 섞이면 읽기 순서가 흐려질 수 있다 | 각 단계에 역할을 이름 붙이고, 대표 폭·확대 상태에서 실제 위계를 확인한다. 단계 수 자체로 실패시키지 않는다 | +| **히어로 = pill 배지 + 거대 헤드라인** (`✨ Now in beta` + H1) | 배지가 실제 상태·시간성·제약을 설명하는지 점검한다 | 배지·문장 안 상태·내비 상태·생략을 비교하고, 첫 행동에 필요한 경우만 남긴다 | | **`_01_ _02_ _03_` 장식 번호 라벨** | 에디토리얼 흉내인데 구조는 없음 | 번호가 순서를 의미할 때만 | -| **본문 letter-spacing 0.05em 이상** | 단어 인식이 느려짐 | 본문 0. 소형 대문자 라벨에만 +0.08em | -| **대형 헤드라인 음수 자간 -0.06em 이상** | 글자가 서로 먹힘 | -0.02em ~ -0.04em | -| **본문 line-height 1.3 미만 / 본문 12px 이하** | 가독성·접근성 실패 | 라틴 본문 line-height 1.5–1.7, 크기 16–18px 권장. **예외**: 고밀도 라틴 UI(대시보드·개발자 도구)에서 13–14px는 의도된 선택일 수 있다 — bun.sh 본문 최다 크기가 13.5px다. 단 **한글에는 이 예외를 적용하지 마라.** 한글은 같은 px에서 라틴보다 작아 보여 16px가 하한이다 | -| **모노스페이스를 장식으로** (코드 아닌 라벨에 `font-mono`) | "테크 느낌"의 저비용 흉내 | 모노는 코드·데이터·식별자에만 | +| **본문/헤드라인 자간이 읽기를 방해함** | 글꼴·언어·크기·폭에 따라 글리프 충돌, 단어 인식, 줄바꿈이 달라진다 | 0, 양수, 음수 자간을 대표 문구·지원 viewport·200% 확대에서 비교한다. 단일 `em` 수치를 보편 기준으로 쓰지 않는다 | +| **본문 크기·행간이 과업에 맞지 않음** | 작은 글자나 촘촘한 행간은 읽기·확대·오류 복구 비용을 높일 수 있다 | 글꼴·언어·콘텐츠 밀도별 후보를 실제 기기·200% 확대·긴 문단에서 검증한다. 16px이나 고정 line-height를 보편 하한으로 선언하지 않는다 | +| **모노스페이스를 장식으로 사용** | 코드·데이터 외 용도에서도 의미·가독성·브랜드 톤을 돕는지 점검한다 | 대상 언어·크기·대비·긴 문자열을 실제 렌더에서 비교한다. 모노스페이스 자체나 용도만으로 실패시키지 않는다. 출처: `references/typography.md` | --- @@ -69,17 +64,14 @@ Show HN 랜딩 1,590개 실측에서 검출률이 가장 높았던 셋이다. ** | 지문 | 왜 문제 | 대신 | |---|---|---| | **표준 골격**: 히어로 → 3열 피처 → 로고월 → 요금제 → FAQ → 푸터 | shadcn/ui 예제·Tailwind UI·Vercel 템플릿의 순서 그대로 | 섹션 순서를 브리프의 설득 논리로 재배열. 예: 문제 제시 → 실제 사용 화면 → 반론 처리 → 가격 | -| **중앙 정렬 히어로** (텍스트 가운데 + CTA 2개 + 아래 스크린샷) | 가장 안전해서 가장 흔함 | 비대칭 그리드(5:7, 7:5), 좌측 정렬 대형 타입, 또는 텍스트를 화면 하단 1/3로 | -| **정확히 3열, 균등 폭 피처 카드** | 콘텐츠가 3개여서가 아니라 3이 예쁘게 떨어져서 3인 것 | 항목 수를 콘텐츠가 정하게. 폭도 중요도에 따라 다르게 | -| **아이콘이 카드 상단 중앙**에 있는 피처 카드 (아이콘 타일 + 제목 + 2문장) | 가장 확실한 단일 지문 | 아이콘을 없애고 숫자·스크린샷·실제 UI 조각으로. 쓸 거면 좌측 인라인 | -| **카드 좌측 3–4px 컬러 스트립** (`border-l-4 border-purple-500`) | em-dash에 맞먹는 신뢰도의 지문 | 삭제. 구분이 필요하면 배경 톤 차이나 여백 | -| **카드 안의 카드 안의 카드** | 시각적 소음. 깊이가 정보와 무관 | 중첩 최대 1단계. 안쪽은 구분선이나 여백으로 | -| **근거 없는 지표 배너** (`10,000+ users · 99.9% uptime · 4.9★`) | 검증 불가한 숫자 나열은 신뢰를 깎는다 | 숫자 1개만, 출처와 함께. 없으면 섹션 삭제 | -| **모든 여백이 같은 값** (전부 `p-6`, 전부 `gap-4`) | 리듬이 없어 강약이 사라짐 | 8pt 그리드 위 4~6종. 섹션 간 : 요소 간 ≈ 1:5 | -| **섹션마다 동일한 상하 패딩** (`py-20` 반복) | 어디가 중요한지 알 수 없음 | 중요한 섹션에 더 많은 여백. 여백이 강조다 | -| **벤토 그리드를 기본 선택지로** | 2026년 기준 표준이라 차별화가 아니다. "고민 안 했음"의 새 신호 | 타일 크기 차이가 정보 위계를 반영할 때만. 아니면 단순 리스트 | -| **콘텐츠가 뷰포트 가장자리에 붙음** | 모바일에서 특히 조악 | 모바일 16–24px, 데스크톱 24–80px 좌우 패딩 | -| **본문 컬럼이 화면 전체 폭** | 한 줄이 100자를 넘어 읽기가 무너짐 | 영문 `max-width: 65ch`. 국문은 25–40자 폭 | +| **중앙 정렬 히어로** (텍스트 가운데 + CTA 2개 + 아래 스크린샷) | 목표 행동·읽기 폭·브랜드의 구도를 돕는지 점검한다 | 중앙·비대칭·좌측 정렬 후보를 같은 콘텐츠와 viewport에서 비교하고, 선택 근거를 기록한다. 중앙 정렬 자체를 실패로 취급하지 않는다. 출처: `references/design-foundations.md`, 프로젝트 브리프 | +| **균등 카드 grid** | 항목 중요도·비교 과업·작은 폭 재배치가 균등 구조와 맞는지 점검한다 | 3열/비대칭/목록을 콘텐츠와 대표 과업에서 비교한다 | +| **아이콘 중심 피처 카드** | 아이콘이 실제 기능 이해를 돕는지, 이미지·수치·UI 증거가 더 나은지 점검한다 | 아이콘 위치·라벨·실제 제품 조각을 비교하고, 전체 카드 hitbox와 접근 가능한 이름을 확인한다 | +| **컬러 스트립·중첩 카드** | 경계와 깊이가 실제 정보 묶음을 나타내는지 점검한다 | 여백·선·표면·중첩을 비교한다. 중첩 횟수와 border 폭은 통과 기준이 아니다 | +| **근거 없는 지표 배너** (`10,000+ users · 99.9% uptime · 4.9★`) | 검증할 수 없는 수치는 사실처럼 제시하면 안 된다 | 출처·측정 조건·기간이 있는 수치만 쓰고, 수가 여러 개여도 목적에 맞는 묶음과 출처를 제시한다 | +| **여백 리듬이 정보 관계를 가림** | 같은/다른 간격이 관련성과 우선순위를 드러내지 못할 수 있다 | 토큰 수·비율을 고정하지 말고, 실제 콘텐츠와 회색조 렌더에서 그룹·섹션 관계를 확인한다 | +| **벤토 그리드를 기본 선택지로** | 타일 크기 차이가 정보 위계나 과업을 설명하지 못할 수 있다 | 벤토·단순 목록·비교 grid를 콘텐츠와 작은 폭 재배치에서 비교한다. 연도별 트렌드 주장은 근거가 아니다 | +| **콘텐츠 여백·본문 폭이 읽기를 방해함** | viewport·글꼴·언어·확대에 따라 줄 길이와 행동 영역이 달라진다 | gutter와 measure 후보를 320px·200% 확대·넓은 폭에서 렌더해 검증한다. 고정 px·ch 범위는 출발값일 뿐이다 | | **푸터가 링크 4열 + 소셜 아이콘 + 카피라이트뿐** | AI가 가장 성의 없이 만드는 곳 | 실제 정보(연락처, 주소, 사업자 정보)를 넣어라. 링크가 4개뿐이면 4열로 만들지 마라 | --- @@ -88,19 +80,15 @@ Show HN 랜딩 1,590개 실측에서 검출률이 가장 높았던 셋이다. ** | 지문 | 왜 문제 | 대신 | |---|---|---| -| **`rounded-2xl shadow-lg p-6` 를 손대지 않고 사용** | shadcn/ui 기본값. AI가 복붙하도록 설계된 값 | radius를 프로젝트 고유값으로(4px 또는 12px). 큰 그림자 대신 1px 보더 + 배경 톤 차이 | -| **모든 요소 border-radius 16px 균일 / 24px 이상 과대** | radius가 결정이 아니라 기본값임을 드러냄. 24px+ 는 블롭처럼 보임 | radius 어휘 2개 (컨테이너 12px, 인터랙티브 8px). 또는 0으로 통일 | -| **1px 회색 보더 + 넓게 퍼진 그림자 동시 사용** | 두 깊이 표현을 겹친 것. 광원 논리 없음 | 하나만 선택 | -| **글래스모피즘 전면 사용** (`backdrop-blur` 카드 다수) | 실기기 FPS 15~30% 하락 | 내비 바·모달로 제한. 카드에는 쓰지 마라 | -| **이모지를 아이콘 대신** (🚀 ⚡ 🎯 ✨) | 즉각적인 아마추어 신호 | 아이콘 세트 하나 고정 (Lucide, Phosphor, Radix). 이모지는 카피 안에서만 | -| **버튼이 항상 2개 나란히** (`Get started` + `Learn more`) | 주 행동을 정하지 못했다는 뜻 | 주 CTA 1개. 보조는 텍스트 링크로 격하 | -| **회색조 로고 6~8개 일렬 로고월** | 대부분 무관하거나 검증 불가 | 실제 고객이면 한 곳의 사례를 문장으로. 없으면 섹션 삭제 | -| **이니셜 원형 아바타 + 텍스트 후기 3개** | 검증 불가한 후기는 신뢰를 깎는다 | 실명 + 직함 + 원문 링크. 없으면 넣지 마라 | -| **가상 질문으로 채운 FAQ 6개** (`How does it work?`) | 실제로 받은 질문이 아님 | 실제 문의 3개. 답변에 구체적 숫자·조건 | -| **요금제 3열 카드 + 가운데 "Most popular" 배지** | 가장 재현율 높은 SaaS 템플릿 | 비교 표, 단일 가격, 또는 계산기 | +| **기본 radius·shadow·padding 조합을 그대로 사용** | 컴포넌트 역할·밀도·브랜드 표면이 토큰과 맞는지 점검한다 | radius·선·그림자·여백 후보를 실제 상태에서 비교하고, 역할 토큰으로 기록한다 | +| **여러 깊이 표현을 겹침** | 경계·그림자·표면 차이가 같은 계층을 중복 설명하는지 점검한다 | 하나 또는 여러 단서를 쓸 수 있다. 포커스·상태·인접 표면에서 계층이 읽히는지 검증한다 | +| **글래스모피즘 전면 사용** (`backdrop-blur` 카드 다수) | backdrop filter의 성능·대비·reduced-transparency 대안이 필요할 수 있다 | 대상 기기의 performance, fallback 표면, 텍스트 대비를 측정하고 역할이 있을 때만 채택한다 | +| **이모지를 아이콘 대신** (🚀 ⚡ 🎯 ✨) | 의미·locale·플랫폼 렌더링·접근 가능한 이름이 역할을 돕는지 점검한다 | 이모지·아이콘 세트·텍스트를 실제 대상과 보조기술에서 비교한다 | +| **여러 CTA가 같은 무게로 경쟁함** | 사용자가 다음 행동을 선택하기 어려울 수 있다 | 행동 우선순위·위험·되돌림에 맞춰 주/보조 CTA와 배치를 정하고 과업 테스트한다 | +| **로고월·후기·FAQ·요금제 템플릿** | 사실·질문·가격 구조가 현재 제품의 증거인지 점검한다 | 검증 가능한 출처와 실제 고객 질문·요금 조건을 사용한다. 개수·열 수·배지 존재로 실패시키지 않는다 | | **펄스 애니메이션 상태 점** (초록 원 깜빡임 + "All systems operational") | 정적 정보에 장식 애니메이션 | 상태가 실제로 바뀔 때만 | -| **자동 스크롤 마퀴** (로고·후기·태그가 끝없이 흐름) | 읽기를 방해하고 주의를 강탈 | 정적 그리드. 많으면 페이지네이션 | -| **hover가 아무 반응 없거나 전 요소가 동일하게 `scale(1.02)`** | 인터랙션을 설계하지 않았다는 신호 | 요소마다 다른 반응(링크는 밑줄, 카드는 배경 톤, 버튼은 명도). **hover는 가장 먼저 설계한다** | +| **자동 스크롤 마퀴** (로고·후기·태그가 끝없이 흐름) | 읽기·움직임 민감도·입력과 충돌할 수 있음 | 목적·정지 수단·감소 모션·성능을 검증하고, 정적 그리드 또는 페이지네이션과 비교 | +| **hover 상태가 과업을 돕지 않음** | 포인터 반응이 행동 가능성·현재 상태·위험을 설명하지 못할 수 있다 | 링크·카드·버튼의 상태를 역할별로 설계하고, touch/keyboard에서도 같은 핵심 단서를 제공한다 | | **편집 불가 히어로 카피 뒤 깜빡이는 커서 `|`** | 타이핑 흉내. 정보 없음 | 삭제 | --- @@ -109,14 +97,11 @@ Show HN 랜딩 1,590개 실측에서 검출률이 가장 높았던 셋이다. ** | 지문 | 왜 문제 | 대신 | |---|---|---| -| **모든 요소에 동일한 fade-in-up** (`opacity 0→1`, `translateY 20px`, 같은 delay) | 모션 언어가 없다는 뜻. 페이지가 계속 떠오르기만 함 | 중요한 것에만. 순차 등장이 필요하면 stagger 40–60ms | -| **인터페이스에 bounce / elastic 이징** | 2010년대 감성. 즉시 촌스러움 | `ease-out` 또는 `cubic-bezier(0.2, 0, 0, 1)` | -| **이미지 hover 시 `scale` 또는 `rotate`** | 생성 UI의 반복 지문 | 이미지는 두고 캡션·오버레이·보더를 변화시켜라 | -| **duration이 전부 300ms** | 마이크로와 연출을 구분 못 함 | 마이크로 100–200 / 트랜지션 200–400 / 연출 600–1200ms | +| **같은 모션을 모든 요소에 적용** | 모션이 상태·관계·우선순위를 설명하지 못하고 읽기를 방해할 수 있다 | 중요한 변화에만 모션을 연결하고, duration/easing은 과업·거리·reduced-motion에서 검증한다. 고정 ms 범위는 출발값이다 | +| **bounce·elastic·scale·rotate 모션** | 장면의 물성·피드백 의미·멀미·성능과 맞는지 점검한다 | 이징과 속성은 실제 상호작용·reduced-motion·저사양 기기에서 비교한다 | | **스크롤 reveal 실패 시 콘텐츠가 `opacity: 0`으로 남음** | JS 실패 시 빈 페이지가 됨 | 기본을 visible로 두고 JS가 숨긴 뒤 보여주는 방식. 또는 CSS scroll-driven animation | -| **`prefers-reduced-motion` 미대응** | 접근성 실패 | 모션 제거 경로를 반드시 제공 | -| **전 섹션 패럴랙스** | 스크롤 감각이 어긋나고 성능 저하 | 한 섹션만, 이동량 10–20px | -| **히어로에 3D/Spline 씬을 이유 없이** | JS 런타임 800KB~2MB. 모바일 4G에서 Core Web Vitals 실패 | 브리프가 요구할 때만. 아니면 정적 렌더 이미지 + 미세 모션 | +| **`prefers-reduced-motion` 미대응** | 사용자 모션 선호를 무시하면 중요한 상태를 이용하기 어려울 수 있다 | `prefers-reduced-motion`에서 비동작 대체 신호와 핵심 과업을 실제로 검증한다 (`evidence-ledger.md`의 `W3C-REDUCED`) | +| **전 섹션 패럴랙스·3D/Spline 씬** | 콘텐츠보다 연출이 우선되거나 저사양·느린 망에서 핵심 경로를 막을 수 있다 | 목적·성능 예산·정적 fallback·정지 제어를 정하고 local proxy와 field data를 구분해 측정한다 | | **로딩할 게 없는데 프리로더 카운터** | 없는 대기를 만듦 | 삭제 | --- @@ -158,7 +143,7 @@ Show HN 랜딩 1,590개 실측에서 검출률이 가장 높았던 셋이다. ** |---|---|---| | 표면은 CSS 가 정하고, 공간은 GPU 가 맡는다 | **텍스처 200KB 를 300바이트가 대신한다** | 구현 → 이득. 수치가 검증 가능 | | Powerful Features | 엑셀 없이 정산이 끝난다 | 명사구 → 주장 | -| 빠르고 안정적인 배포 | 배포 실패가 롤백까지 90초 | 형용사 → 측정치 | +| 빠르고 안정적인 배포 | 실제 배포·롤백 결과와 조건 | 형용사 → 측정치. 예: 검증된 `p95 롤백 시간`, 기간, 환경 | ### 데모가 주장을 증명하는지 재라 @@ -189,7 +174,7 @@ B 라면, 반응형에서 매번 바뀌는 것을 지목한 것이다. 지목하 | 지문 | 왜 문제 | 대신 | |---|---|---| -| **`It's not X, it's Y` / `단순한 X가 아니라 Y입니다`** | ChatGPT 최대 지문. 400단어에 3번씩 나옴 | 그냥 Y를 말해라. 대조가 필요하면 비교 대상을 실명으로 | +| **`It's not X, it's Y` / `단순한 X가 아니라 Y입니다`** | 대조가 실제 차이·조건을 설명하지 못하면 문장이 비어 보일 수 있다 | Y를 직접 말하거나, 필요할 때 비교 대상과 검증 조건을 명시한다 | | **스타카토 3연타** (`No fluff. No filler. No BS.` / `빠르게. 정확하게. 간단하게.`) | 리듬으로 내용 없음을 감춤 | 한 문장으로 구체적으로 | | **em-dash(—) 한 문단에 2회 이상** | AI 문장 리듬의 대표 지문 | 마침표로 끊거나 쉼표로. 한국어에서 특히 부자연스럽다 | | **`In today's fast-paced digital landscape...`** 도입 | 아무 말도 하지 않는 문단 | 첫 문장부터 본론. 도입 문단 삭제 | @@ -201,9 +186,9 @@ B 라면, 반응형에서 매번 바뀌는 것을 지목한 것이다. 지목하 | `Streamline / Empower / Supercharge / Unlock / Leverage / Seamless / Elevate` | 아무 제품에나 붙는 동사. 정보량 0 | 제품이 실제로 하는 동작 동사 (`정산한다`, `병합한다`, `4일을 6시간으로 줄인다`) | | `world-class / cutting-edge / enterprise-grade / best-in-class` | 자기 평가 형용사는 증거가 아니다 | 인증명, 벤치마크 수치, 고객사 실명 | | `Build the future of X` / `Your all-in-one platform` / `Scale without limits` | 경쟁사 이름으로 바꿔도 성립 | 치환 테스트를 통과하는 문장 (예: "Financial infrastructure for the internet") | -| 헤지 표현 (`may help you`, `~할 수 있습니다`) | 확신 없음 = 신뢰 없음 | 단정하거나 조건을 명시 (`10명 이하 팀에서는`) | +| 헤지 표현 (`may help you`, `~할 수 있습니다`) | 불확실성의 범위가 숨겨지면 독자가 주장 강도를 판단하기 어렵다 | 검증된 내용은 명확히 쓰고, 불확실하면 조건·근거·한계를 명시한다 | | CTA가 `Get Started` / `Learn More` / `시작하기` | 무엇이 시작되는지 알 수 없음 | 결과를 말하는 CTA (`무료로 30일 써보기`, `가격표 보기`) | -| 모든 숫자가 어림수 (`10,000+`, `99.9%`, `50% faster`) | 검증 불가 | 정확한 수치와 측정 조건 (`p95 응답 240ms, 2026-07 기준`) | +| 출처·조건 없는 숫자 (`10,000+`, `99.9%`, `50% faster`) | 사실처럼 보이는 주장의 검증 경로가 없다 | 정확/범위 수치 모두 출처·측정 조건·기간을 붙인다. 조건 없는 수치는 제거하거나 예시임을 라벨링한다 | | 모든 헤딩이 명사구 (`Powerful Features`, `Simple Pricing`) | 헤딩이 정보를 전달하지 않음 | 헤딩에 주장을 담아라 (`엑셀 없이 정산이 끝난다`) | | 레이블·서브레이블·헬퍼가 같은 말 3번 (`이메일` / `이메일 주소` / `이메일 주소를 입력하세요`) | 화면 소음 | 하나만 남긴다 | | 이모지로 시작하는 불릿 (`✅ 빠른 속도`) | 정보 위계를 이모지로 대체 | 일반 불릿 또는 문장으로 | @@ -214,10 +199,7 @@ B 라면, 반응형에서 매번 바뀌는 것을 지목한 것이다. 지목하 | 지문 | 왜 문제 | 대신 | |---|---|---| -| **랩탑 앞에서 웃는 다국적 팀 스톡 사진** | 방문자는 스톡을 알아보고 **신뢰도가 실제로 하락**한다 | 실제 팀 사진, 실제 작업 공간, 또는 사진 없이 타입으로 | -| **떠 있는 3D 추상 블롭·기하 도형** | 콘텐츠 부재를 덮는 장식 | 제품 실제 화면, 데이터 시각화, 또는 여백 | -| **AI 생성 일러스트** (지나치게 매끄럽고 대칭적) | 결함이 없어도 톤에서 티가 남 | 일러스트 시스템을 직접 정의하거나 아예 쓰지 않는다 | -| **일반 SVG 도형을 조합한 히어로 그래픽** | 플레이스홀더 클립아트처럼 읽힘 | 제품 UI 조각을 실제로 렌더 | +| **스톡 사진·추상 3D·AI 일러스트·일반 SVG 그래픽** | 이미지가 제품 증거·브랜드·정보 이해 대신 빈 자리를 메우는지 점검한다 | 실제 사진·제품 UI·데이터·일러스트·여백 중 과업을 가장 잘 설명하는 것을 선택하고, 출처·라이선스·alt·크롭을 검증한다 | | **아이콘 세트가 섞임** (라인·필·이모지 혼재) | 시스템이 없다는 증거 | 세트 하나 고정. 굵기·크기·광학 정렬 통일 | | **`src`가 비었거나 깨진 이미지 태그** | 검증 없이 배포된 흔적 | 배포 전 이미지 로드 전수 확인 | | **모든 이미지가 같은 비율의 둥근 사각형** | 그리드를 이미지에 강제 | 콘텐츠에 맞는 비율. 풀블리드 1장을 섞으면 리듬이 생긴다 | @@ -226,25 +208,19 @@ B 라면, 반응형에서 매번 바뀌는 것을 지목한 것이다. 지목하 ## 8. 한글 조판 (한국어 프로젝트 필수) -서구 갤러리에는 없는 규칙이다. **영문 기준을 그대로 쓰면 전부 깨진다.** +한국어는 글꼴·줄바꿈·fallback·문장 리듬을 실제 콘텐츠와 지원 기기에서 별도로 검증한다. 영문 출발값을 그대로 복사하지 않는다. | 규칙 | 값 | 이유 | |---|---|---| -| **한글 폰트 미지정 금지** | `Pretendard` 또는 `본고딕` 명시 | 지정하지 않으면 시스템 기본(맑은 고딕 / Apple SD 산돌고딕)으로 떨어지고, 그 자체가 완성도 미달 신호다 | -| **line-height를 영문보다 높게** | 본문 **1.6–1.8** | 한글은 글자 밀도가 높아 1.5로는 답답하다 | -| **음수 자간 금지** | 본문 0, 대형 헤드라인만 -0.01 ~ -0.02em | 한글은 고정폭에 가까워 음수 자간이 즉시 뭉개진다 | -| **`word-break: keep-all`** | `overflow-wrap: break-word`와 함께 | 기본값은 단어 중간에서 줄바꿈되어 어색하다 | -| **한 줄 25–40자** | 영문 45–75자 기준을 쓰면 너무 길다 | 컨테이너 폭을 좁혀라 | -| **라틴 폰트를 폴백 앞에** | `font-family: 'Satoshi', 'Pretendard', sans-serif` | 한글 폰트를 앞에 두면 라틴 문자까지 그 폰트로 그려진다 | -| **번역투 금지** | `~를 통해`, `~에 대한`, `~에 있어서` | 영문 AI 카피를 기계 번역한 티가 난다. `~로`, `~의`, `~에서`로 | +| **한글 글꼴과 fallback 미확인** | 한국어 glyph coverage·줄바꿈·숫자·운영체제 fallback이 과업에 맞는지 점검한다 | 시스템 한글 폰트를 포함해 실제 지원 기기에서 긴 제목·본문·숫자를 렌더하고, 선택·라이선스·fallback metric을 기록한다. 시스템 폰트 사용만으로 완성도 미달로 취급하지 않는다. 출처: `references/typography.md`, 프로젝트 브리프 | +| **한글 line-height 후보** | 글꼴·크기·문단 길이·화면 폭에 따라 읽기 리듬이 달라진다 | 1.6–1.8을 포함한 후보를 실제 본문·확대 상태에서 비교한다. 특정 값이 보편 기준은 아니다 | +| **한글 음수 자간** | 글리프 충돌·단어 인식·확대 상태에서의 읽기성을 점검한다 | 본문·대형 제목·지원 viewport·200% 확대에서 실제 렌더를 비교한다. 음수 자간의 존재만으로 실패시키지 않고, 충돌이나 읽기 저하가 확인되면 조정한다. 출처: `references/typography.md`, 프로젝트 렌더 검증 | +| **줄바꿈·measure·fallback 순서** | 단어 단위 줄바꿈, 라틴/한글 숫자 렌더, 폭은 글꼴과 콘텐츠에 따라 달라진다 | `word-break`, `overflow-wrap`, measure, fallback 순서를 대표 문단·긴 단어·숫자·지원 기기에서 비교한다. 고정 글자 수나 순서를 규범으로 쓰지 않는다 | +| **번역투 가능성** | 문맥·대상 독자와 맞지 않는 직역은 의미를 흐릴 수 있다 | 실제 독자와 용어 체계에 맞춰 문장을 다듬고, 제시 표현을 기계적 치환 규칙으로 쓰지 않는다 | -**한글 폰트 선택** -- 기본값: **Pretendard** (SIL OFL, Thin~Black 9단계, 가변). CDN: `https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/variable/pretendardvariable.min.css` -- 명조·에디토리얼: **마루 부리** (세리프 부활 트렌드의 한글 대응) -- 수치 많은 UI: **Spoqa Han Sans Neo** -- 다국어 안정성: **본고딕 / Noto Sans KR** -- 캠페인 헤드라인: 배민 도현체·여기어때 잘난체 — **B2C 한정. B2B에 쓰면 즉시 아마추어** -- 나눔고딕은 너무 흔하다. 폴백으로만. +**한글 폰트 후보** +- Pretendard, 마루 부리, Spoqa Han Sans Neo, 본고딕/Noto Sans KR 등은 목적·glyph coverage·라이선스·fallback·전송량을 비교할 후보다. +- 캠페인 display 폰트도 업종 자체로 배제하지 않는다. 실제 문구·브랜드·작은 크기·지원 기기에서 읽기와 인상을 검증한다. --- @@ -281,11 +257,11 @@ It's not .* it's **한글 프로젝트 추가 검사** ``` -# 있어야 하는 것 — 없으면 실패 +# 확인 후보 — 프로젝트 계약과 실제 렌더로 평가 word-break:\s*keep-all Pretendard|Noto Sans KR|본고딕|마루부리 -# 없어야 하는 것 +# 세밀하게 확인할 후보 — 프로젝트 계약과 실제 렌더로 평가 letter-spacing:\s*-0\.0[3-9] (한글 본문) ~를 통해|~에 대한|에 있어서 ``` @@ -306,10 +282,10 @@ letter-spacing:\s*-0\.0[3-9] (한글 본문) ```js await page.evaluate(() => el.scrollIntoView({ block: 'center' })); -await page.waitForTimeout(1600); // 반드시 애니메이션이 끝난 뒤에 잰다 +await page.waitForTimeout(1600); // 프로젝트의 모션 종료 조건을 확인한 뒤 잰다 ``` -**② `sharp(...).extract(...).stats()` 는 잘라내기를 무시하고 원본 전체를 잰다.** +**② `sharp(...).extract(...).stats()` 사용 결과가 의도한 crop 통계를 반영하는지 확인한다.** 영역을 여섯 군데 잘라 밝기를 쟀는데 여섯 개가 **소수점까지 똑같이** 나왔다. 전부 원본 전체 평균이었기 때문이다. 값이 수상하게 균일하면 도구를 먼저 의심해라. @@ -324,12 +300,10 @@ const s = await sharp(buf).stats(); // 잘라낸 버퍼를 다시 물려야 겹쳐 있어서 "필터가 안 붙는다" 는 잘못된 결론이 나왔다. 멈춘 프레임을 그대로 찍으려면 그 옵션을 **빼야** 한다. -> 여기서 더 중요한 결론이 나왔다 — **움직임으로 증명하려 들지 마라.** -> 지나가는 프레임은 방문자도 놓치고 측정도 매번 다른 값을 준다. -> 정지된 before/after 두 장이 더 정확하게 말한다. +> 움직임이 핵심 주장을 전달한다면 정지 상태의 대체 증거와 함께 검증한다. 지나가는 프레임은 캡처 시점에 따라 달라질 수 있으므로, before/after 정지 캡처가 비교에 적합한 경우가 많다. **④ 개별 요소의 높이로는 "여러 줄에 걸친 것" 을 못 잡는다.** -"컨트롤이 2줄로 접히는가"(하드 게이트 6)를 이렇게 쟀다. +"컨트롤 행이 지원 폭에서 읽거나 조작할 수 없게 되는가"를 이렇게 쟀다. ```js // 틀렸다 — 이 검사는 "접힘 0" 이라고 답한다 @@ -348,16 +322,15 @@ new Set([...document.querySelectorAll('.nav a')] ``` 같은 함정이 갤러리·태그 목록·버튼 그룹 어디에나 있다. -**"몇 줄인가" 를 물을 때는 높이가 아니라 위치를 세라.** +**"몇 줄인가"를 물을 때는 컨테이너의 위치 분포와 요소 높이를 함께 본다.** **⑤ 눈으로 의심한 것이 실측에서 뒤집힐 수 있다.** WebGL 판이 본문 뒤를 지나가는 화면을 보고 "대비가 죽었다" 고 판단해 캔버스를 어둡게 만들려 했다. 스크린샷에서 글자 사이 빈 띠의 배경 휘도를 재보니 최악 지점이 **9.83:1** — AAA(7:1)를 넘었다. 고칠 필요가 없었다. -밝은 것과 대비가 낮은 것은 다르다. **화려한 배경 위의 대비는 인상이 아니라 숫자로 판정해라.** +밝은 것과 대비가 낮은 것은 다르다. 화려한 배경 위 텍스트는 실제 배경 합성 상태의 contrast ratio와 읽기 관찰을 함께 확인한다. -> 캔버스 위에서는 `readPixels` 가 안 통한다(three.md 참고). 스크린샷을 찍어 -> **글자를 피한 빈 띠**의 평균 휘도를 재는 것이 유일하게 맞는 방법이다. +> 캔버스 접근 가능성은 렌더링 구성에 따라 다르다. 스크린샷 기반 측정은 한 방법이며, 텍스트와 배경의 실제 합성 상태를 대표하는 표본을 골라 계산한다. --- @@ -371,8 +344,7 @@ sticky 캔버스가 끝까지 돌려면 목록 뒤에 스크롤 구간이 필요 문장 자체는 틀리지 않았다. 다만 **읽는 사람이 그 자리에서 기대하는 말이 아니다.** 사용자가 처음 한 말이 "이건 왜 있는 거예요" 였다. -레이아웃이 필요해서 쓴 문장은 예외 없이 겉돈다. 여백이 필요하면 -`padding` 을 주고, 그 자리에 놓을 말이 정말 있을 때만 문장을 쓴다. +레이아웃을 위해 추가한 문장이 콘텐츠 역할을 하지 않으면 겉돌 수 있다. 여백과 문장 중 어느 것이 목적을 더 잘 충족하는지 검토한다. > 자가 진단: 이 문장을 지우면 **레이아웃이 깨지는가, 뜻이 빠지는가?** > 레이아웃만 깨진다면 그 문장은 여백의 대역이고, 여백으로 바꿔야 한다. @@ -393,7 +365,7 @@ sticky 캔버스가 끝까지 돌려면 목록 뒤에 스크롤 구간이 필요 **곁가지 하나가 본론의 1.3 배, 첫인상이 가장 짧다.** 분량 배분이 중요도와 정반대다. 사용자 표현으로는 "정신이 없어 보인다". -부분 수정을 몇 번 하고 나면 반드시 전체를 다시 재라. +부분 수정이 누적되면 전체 비율·읽기 순서·핵심 과업을 다시 측정한다. ```js // 섹션별 화면 수 — 배분이 우선순위와 맞는지 한 번에 보인다 @@ -453,26 +425,22 @@ sticky 캔버스가 끝까지 돌려면 목록 뒤에 스크롤 구간이 필요 | 7 이미지 | | | | 8 한글 조판 | | | -**통과 조건 — 전부 예여야 한다** -- [ ] 최우선 3종(보라 CTA / 전체 대문자 / 1·2·3 단계) 중 걸린 것이 없다 -- [ ] grep 검사에서 나온 항목을 전부 해소했다 +**검토 조건 — 근거와 결과를 남긴다** +- [ ] 반복 후보(보라 CTA / 전체 대문자 / 1·2·3 단계)를 발견했으면 프로젝트 계약·브리프·실제 렌더 근거로 유지·수정·제거를 결정했다 +- [ ] grep 검사에서 나온 항목은 자동 실패로 취급하지 않고, 출처·관찰·결정·검증을 기록했다 - [ ] 카피가 경쟁사 치환 테스트를 통과한다 - [ ] 모션을 전부 끄고도 페이지가 작동한다 - [ ] 강조색을 지워도 페이지가 읽힌다 - [ ] 색을 회색조로 바꿔도 위계 순서가 비즈니스 우선순위와 일치한다 - [ ] 키보드만으로 전 인터랙션이 가능하고 포커스 링이 보인다 -- [ ] (한국어) 한글 폰트가 명시되어 있고 `word-break: keep-all`이 있다 -- [ ] 모든 섹션에서 제목→본문 간격이 **같은 값**이다 (0px 인 섹션이 하나도 없다) +- [ ] (한국어) 글꼴·fallback·줄바꿈 규칙이 실제 지원 기기와 긴 제목·본문·숫자에서 읽히는지 검증하고, 프로젝트 계약에 선택 근거를 남겼다 +- [ ] 제목→본문 간격이 각 섹션의 정보 관계를 설명하고, 의도하지 않은 0px·겹침·불균형이 없는지 실제 렌더에서 확인했다 - [ ] 화면에 적은 수치는 **전부 그 화면을 캡처해 잰 값**이다 (추정치·기대치가 섞여 있지 않다) -- [ ] 이미지 높이를 실제로 쟀다 — 원본 픽셀 높이가 그대로 나오면 `aspect-ratio` 가 죽은 것이다 +- [ ] 이미지의 실제 표시 비율·크롭·해상도가 의도한 콘텐츠 역할과 화면 폭에 맞는지 측정했다. 원본 픽셀과의 일치만으로 `aspect-ratio` 동작을 판정하지 않는다 - [ ] 내비·버튼 그룹의 **행 수를 `top` 값으로** 셌다 (개별 높이로는 못 잡는다) **판정** -| 걸린 개수 | 판정 | -|---|---| -| 0–1 | 통과 | -| 2–3 | 해당 항목 수정 후 재검 | -| **4 이상** | **폐기. reference-method.md §3부터 다시** | +후보 수를 점수나 자동 통과 기준으로 쓰지 않는다. 각 후보마다 `프로젝트 계약`, `출처 ID`, `적용 조건`, `실제 렌더 관찰`, `유지·수정·제거 결정`, `검증 결과`를 남긴다. WCAG 성공 기준 또는 실제 기능 실패가 있으면 그 항목은 해결 전 통과로 선언하지 않는다. 그 밖의 스타일 후보는 과업·브랜드·위계·성능의 증거로 판정한다. -> 근거: research/references/04-ai-slop-signatures.md, 03-trends-2026.md (조사일 2026-08-20) +> 근거와 한계: `references/evidence-ledger.md`, `references/design-foundations.md`, 프로젝트 브리프와 렌더 검증 diff --git a/packages/skill/references/art-direction.md b/packages/skill/references/art-direction.md new file mode 100644 index 0000000..3ea148f --- /dev/null +++ b/packages/skill/references/art-direction.md @@ -0,0 +1,149 @@ +# art direction — 화면에서 판단할 수 있게 방향을 정하는 법 + +2단계에서 방향을 고르고, 3단계 토큰과 4단계 구현 전에 읽는다. 아트디렉션은 스타일 이름이나 색 견본을 고르는 일이 아니라, 실제 카피·자산·과업을 어떤 화면 질서로 보일지 결정하는 일이다. +이 문서는 원전을 그대로 옮긴 규칙도, 특정 효과의 실험적 성과 주장도 아니다. 아래 출처와 프로젝트 렌더를 바탕으로 합성한 작업 절차다. + +| 이 문서를 여는 때 | 여기서 얻는 결정 | 답이 부족할 때 필요한 추가 근거 | +|---|---|---| +| 첫 화면의 정보 순서·이미지 역할·섹션 리듬이 아직 불명확할 때 | 방문자 질문, 주장, 증거, 다음 행동과 그것의 화면 비중 | 실제 사용자 과업, 콘텐츠 소유자 확인, 브랜드 자산·권리·사실성, 지원 기기·상태별 렌더 | +| 같은 스타일 안에서 구도·크롭·타입·밀도를 정해야 할 때 | 초점, 정렬선, 여백 역할, 원본·배포 크롭, proof sheet의 채택 근거 | 원본 자산, 실제 카피·수치·상태, 이미지 설명·접근성 요구, 배포 비율과 실제 viewport | +| 한 선택의 품질이나 과업 적합성을 판정할 때 | 동일 조건에서 비교할 관찰·원인 후보·한 가지 수정 | 프로젝트 계약, 사용성·접근성 규범, 기능·성능 측정, 필요하면 해당 분야의 1차 출처 또는 현장 검증 | + +출처가 현재 질문을 직접 지지하지 않으면 이 문서의 예시를 규칙으로 확대하지 않는다. 필요한 과업·자산·규범의 1차 근거를 새로 확인하고, 그 범위와 한계를 프로젝트 기록에 남긴다. + +--- + +## 1. 대안을 언제 비교할지 먼저 정한다 + +큰 불확실성이 있는 디자인 결정에서만 구조가 다른 대안을 작게 비교한다. 예를 들어 첫 화면이 공간의 분위기를 먼저 보여야 하는지, 예약 가능한 시간과 위치를 먼저 보여야 하는지처럼 정보 순서가 바뀌는 결정이다. +이때의 대안은 색·서체·사진만 바꾼 변형이 아니라, 질문에 답하는 순서와 화면 골격이 달라야 한다. + +| 상황 | 할 일 | 하지 않을 일 | +|---|---|---| +| 브랜드·과업·정보 우선순위가 아직 불확실함 | 서로 다른 구조를 가진 소수 안을 같은 콘텐츠로 비교 | 시안 수를 의무화하거나 장식만 바꾼 안을 세기 | +| 브랜드 방향과 핵심 구조가 확정됨 | 기존 방향 안에서 카피·크롭·밀도 후보를 고름 | 확정된 정체성을 매 화면마다 다시 경쟁시킴 | +| 국소 컴포넌트나 상태를 다듬음 | 그 화면의 과업과 상태를 기준으로 후보를 비교 | 전체 랜딩의 AIDA나 히어로 구도를 복사 | + +결정마다 `무엇이 불확실한가 / 구조 차이는 무엇인가 / 같은 조건은 무엇인가 / 채택 이유는 무엇인가`를 `design.md`에 남긴다. 대안 탐색은 좁은 문제를 확인하는 절차이며, 더 많은 안이 더 좋은 결과를 보장하지 않는다. + +## 2. 콘텐츠 편집표로 화면의 일을 정한다 + +레이아웃 전에 각 화면 또는 섹션이 답해야 할 질문을 적는다. 마케팅 서사에 유용한 순서가 있어도 기능 화면·운영 화면에 AIDA를 강요하지 않는다. + +| 방문자 질문 | 핵심 주장 | 실증 콘텐츠 | 행동 또는 다음 정보 | 화면 비중 | +|---|---|---|---|---| +| 여기는 무엇이며 나에게 왜 중요한가 | 한 문장 가치 | 실제 결과, 공간, 사례, 수치의 출처 | 더 보기, 예약, 시작 | 첫 화면에서 가장 크게 | +| 이 주장을 믿을 근거가 있는가 | 구체적 차이 | 과정, 비교, 고객 맥락, 검증 가능한 정보 | 세부 근거로 이동 | 주장 다음에 충분히 | +| 지금 무엇을 해야 하는가 | 다음 행동의 비용과 결과 | 가격, 일정, 준비물, 현재 상태 | 실행, 저장, 문의 | 행동 직전에 읽히게 | +| 일이 진행 중인 운영 화면인가 | 지금의 상태와 예외 | 목록, 필터, 경고, 최근 변경 | 처리, 검토, 복구 | 빈도·위험도에 비례 | + +행마다 실제 문장, 실제 데이터, 실제 자산을 붙인다. 주장이 있는데 실증 콘텐츠가 없으면 장식으로 덮지 말고 주장 범위를 줄이거나 근거를 확보한다. +행동이 없으면 CTA를 억지로 추가하지 말고 다음에 필요한 정보를 우선한다. + +## 3. 실제 재료로 구도 축을 고른다 + +빈 로렘입숨과 회색 상자로 구도를 확정하지 않는다. 실제 한글·영문 카피, 제목 길이, 숫자, 사진·도표·상태를 넣고 아래 축 중 필요한 것을 고른다. + +| 구도 축 | 질문 | 렌더에서 확인할 것 | +|---|---|---| +| 초점 | 처음 시선이 닿아야 하는 것은 무엇인가 | 제목·행동·증거 중 하나가 경쟁 없이 먼저 읽히는가 | +| 정렬선 | 무엇이 같은 관계로 묶이는가 | 제목, 수치, 표, 행동의 시작선이 의미를 드러내는가 | +| 큰 면과 작은 정보 | 무엇이 면적을 가져야 하는가 | 큰 사진·제목이 보조 정보의 읽기를 막지 않는가 | +| 여백의 역할 | 비어 있는 면이 무엇을 돕는가 | 분리, 대기, 방향, 강조 중 이유 없이 빈 곳은 없는가 | +| 이미지와 텍스트 관계 | 둘은 같은 주장인가 다른 역할인가 | 텍스트가 사진 속 사실을 과장하거나 반복하지 않는가 | + +색만 바꾼 안은 별도 구조안이 아니다. 사진 위 텍스트는 대비만 보지 말고, 피사체·캡션·행동이 같은 의미를 말하는지 확인한다. +수치·상태·가격·주소처럼 수정 가능하고 사실 검증이 필요한 정보는 이미지 안에 굽지 않는다. + +## 4. 이미지는 원본과 배포 크롭을 함께 검토한다 + +이미지의 품질은 원본 파일만으로 판정하지 않는다. 원본, 데스크톱 배포 크롭, 모바일 배포 크롭을 나란히 놓고 슬롯별로 아래를 기록한다. + +| 기록 항목 | 확인 질문 | +|---|---| +| 주피사체 | 이 화면에서 반드시 남아야 하는 사람·제품·공간·행동은 무엇인가 | +| 시선 방향 | 인물·물체·선이 카피나 다음 행동으로 시선을 보내는가 | +| 사라지는 맥락 | 크롭 뒤에 장소·관계·사용 장면의 뜻이 사라지지 않는가 | +| 데스크톱 초점 | 넓은 비율에서도 핵심이 안전 영역에 남는가 | +| 모바일 초점 | 좁은 비율에서 핵심이 폴드 아래·텍스트 뒤로 밀리지 않는가 | +| `object-position` | 같은 원본을 쓸 때 각 비율에서 의도한 초점 좌표가 명시됐는가 | + +한 장의 원본을 모든 슬롯에 억지로 쓰지 않는다. 반대로 비교를 위해 여러 이미지를 인위적으로 생성할 필요도 없다. 필요한 슬롯에 실제로 다른 구도가 필요할 때만 별도 자산을 고른다. +이미지 조달·진실 라벨·대체 텍스트의 세부는 [images.md](images.md)와 `trustworthy-showcases.md`를 따른다. + +## 5. 타입은 proof sheet에서 결정한다 + +폰트 이름, 문자 견본, 한 줄 영문만으로 위계를 확정하지 않는다. 새 서체·타입 계층을 고르거나 변경이 줄바꿈·밀도·상태 인지에 영향을 줄 때만, 같은 폭과 크기에서 실제 콘텐츠를 넣은 proof sheet를 만든다. + +- 프로젝트에서 쓰는 언어의 본문과 긴 제목 +- 표시하는 가격·날짜·비율·음수·증감 같은 숫자 +- 실제로 쓰는 선택됨, 오류, 비활성, 처리 중 같은 상태 텍스트 +- 변경 범위에 있는 버튼 라벨, 표 머리글, 카드 보조 정보 + +그 뒤 줄바꿈, 숫자 폭, 밀도, 상태의 구분, 확대 상태에서의 읽기를 비교한다. 서체 선택·로딩·폴백 메트릭·라이선스와 한글 조판의 정본은 [typography.md](typography.md)다. +proof sheet는 멋의 순위를 매기는 카드가 아니라, 이 프로젝트의 실제 글자가 어떤 구도를 요구하는지 확인하는 증거다. + +## 6. 전체 스크롤의 리듬을 편집한다 + +반복은 나쁜 것이 아니다. 같은 데이터·행동·비교 규칙을 읽게 하는 반복은 보존한다. 문제는 역할이 다른 섹션이 같은 제목·카드·장식만 반복해서 스크롤이 정보를 더하지 않을 때다. + +1. 각 섹션의 의미 단위와 다음 행동을 목록화한다. +2. 같은 의미 단위는 같은 리듬을 유지한다. +3. 정보 관계가 달라지면 필요한 순서·밀도·배치를 조정한다. +4. 장식만 변주해 다른 섹션처럼 보이게 하지 않는다. +5. 긴 페이지는 폴드마다 무엇이 새로 이해되거나 진행되는지 실제 스크롤에서 확인한다. + +리듬을 바꾸는 이유는 새 배경색이나 카드 모양이 아니라 독자의 질문과 과업이 바뀌었기 때문이다. + +## 7. 실제 화면에서 한 번에 하나씩 비평한다 + +소스 코드나 모형을 보고 완성도를 선언하지 않는다. 같은 viewport, 상태, 카피, 자산에서 전후 렌더를 비교한다. + +1. **관찰**: 무엇이 먼저 읽히는지, 어디에서 시선·행동·관계가 끊기는지 적는다. +2. **원인 후보**: 구도, 카피 길이, 크롭, 위계, 상태 밀도 중 가능한 원인을 좁힌다. +3. **한 가지 수정**: 한 번에는 한 원인 가설만 바꾼다. +4. **동일 조건 비교**: 같은 폭·상태·콘텐츠에서 전후를 보고 유지·되돌림·다음 수정을 결정한다. + +비평은 아래 세 관점에서 따로 적는다. + +| 관점 | 확인 질문 | 주장하지 말 것 | +|---|---|---| +| 독창성 | 이 브랜드·과업·자산에서만 나올 이유가 보이는가 | 낯선 효과 하나가 고유성을 증명한다 | +| 완성도 | 정렬, 타입, 크롭, 여백, 상태가 같은 규칙으로 끝까지 유지되는가 | 자동 심미 점수가 품질을 증명한다 | +| 과업 | 사용자가 다음 정보와 행동을 예측·완료·복구할 수 있는가 | 시각 점수만으로 성공률이나 전환 인과를 증명한다 | + +완료 판정은 세 갈래로 분리한다: 사용성·접근성 규범, 프로젝트의 기능·성능 계약, 시각 평가. +한 갈래의 통과가 다른 갈래의 통과를 대신하지 않는다. + +## 8. 같은 규칙도 화면에 따라 다르게 쓴다 + +### 공간 포트폴리오 + +방문자 질문은 “이 장소의 분위기와 작업 범위가 내 프로젝트에 맞는가”다. +첫 화면은 실제 공간의 주피사체와 짧은 주장으로 관계를 만들고, 다음 섹션은 재료·과정·완성 공간의 증거로 이어진다. +카피가 길지 않다면 큰 이미지 면과 넓은 여백이 장소의 규모·빛·질감을 읽게 할 수 있다. +모바일 크롭에서 창, 사람의 행동, 재료 중 무엇이 사라지면 장소의 뜻이 바뀌는지 기록한다. +문의 행동은 검증 가능한 범위·일정 정보 다음에 둔다. + +### 밀집 운영 화면 + +방문자 질문은 “지금 무엇이 막혔고 어떤 항목부터 처리해야 하는가”다. +첫 화면은 분위기 사진보다 상태, 위험, 우선순위, 가능한 행동을 먼저 둔다. +같은 종류의 행·필터·수치는 반복 구조를 유지해야 비교가 빠르며, 여백은 장식보다 군집과 오류 복구를 돕는다. +큰 영문 타이틀이나 풀블리드 이미지를 공간 포트폴리오처럼 복사하면 상태 정보와 행동이 밀릴 수 있다. +타입 proof sheet에는 긴 한국어 상태, 숫자 열, 비활성·오류 라벨을 넣어 밀도에서 실제로 비교한다. + +두 화면 모두 실제 콘텐츠, 구도 축, 원본과 크롭, 동일 조건 비평을 쓴다. +다만 질문과 실증 콘텐츠가 다르므로 면적·정렬선·리듬·행동 순서를 같게 만들 이유는 없다. + +## 근거와 적용 경계 + +| ID | 출처와 역할 | 이 문서에서의 사용 한계 | +|---|---|---| +| DC-DIAMOND | [Design Council, Double Diamond](https://www.designcouncil.org.uk/resources/the-double-diamond/) · practice | 불확실한 문제에서 소규모 대안 탐색과 검증의 절차 참고이며, 시안 수나 성과의 근거가 아니다 | +| VA-MOFFAT | [V&A, Curtis Moffat: Working Methods](https://www.vam.ac.uk/articles/curtis-moffat-working-methods) · case · 2025-09-24 | contact print와 crop을 한 작업 사례로 참고하며, 모든 프로젝트의 이미지 절차로 일반화하지 않는다 | +| CH-DESCRIPTION | [Cooper Hewitt image description guidelines](https://www.cooperhewitt.org/cooper-hewitt-guidelines-for-image-description/) · practice | 중요한 정보부터, 공간 관계를 따라 설명하는 방식을 참고한다. alt 단어 수를 고정하지 않는다 | +| LUPTON-POSTERS | [How Posters Work](https://www.cooperhewitt.org/publications/how-posters-work/) · bibliography | 책의 소개·서지 확인만 한다. 원문을 읽었거나 개념을 인용했다는 주장이 아니다 | +| GOV-IMAGES | [GOV.UK Design System: images](https://design-system.service.gov.uk/styles/images/) · practice | 정부 서비스 범위에서 과업 정보·맥락·alt를 다루는 지침이다. 감성 사진 금지를 상업 브랜드에 일반화하지 않는다 | + +출처의 상세 기록 형식과 사용성·접근성·성능 검증은 [evidence-ledger.md](evidence-ledger.md), [design-foundations.md](design-foundations.md), 프로젝트의 `design.md`를 함께 따른다. diff --git a/packages/skill/references/audit-gate.md b/packages/skill/references/audit-gate.md index 7c540bb..aaedd2a 100644 --- a/packages/skill/references/audit-gate.md +++ b/packages/skill/references/audit-gate.md @@ -2,9 +2,9 @@ 5단계(감사)를 **완료 선언의 게이트**로 만드는 문서다. 핵심 원칙은 하나다: -> **완료의 정의는 게이트 통과다. 사용자를 QA 로 쓰지 않는다.** +> **완료는 기능·프로젝트 계약·렌더 시각 평가가 함께 확인된 상태다. 사용자를 QA 로 쓰지 않는다.** -"고쳤다"는 말의 근거는 검증 리포트의 수치다. 게이트가 실패 항목을 내면 4단계로 돌아가 고치고 **다시 전체를 돌린다** — 고친 것만 재검하지 않는다. 이 루프(구현 → 게이트 → 실패 → 수정 → 재게이트)가 designpaca의 기본 동작이며, 게이트 없이 6단계로 넘어가는 것은 경로 무관 금지다. +"고쳤다"는 말의 근거는 검증 리포트의 수치와 실제 렌더 평가다. 게이트가 실패 항목을 내면 4단계로 돌아가 고치고, 변경한 영역과 연결된 회귀 위험을 다시 검사한다. 프로젝트가 정한 전체 검증 계약은 그대로 수행한다. 이 루프(구현 → 게이트 → 실패 → 수정 → 재게이트)가 designpaca의 기본 동작이며, 기능·접근성 자동 검사와 시각 평가(위계·브랜드·타입·구도·이미지·리듬)는 별도 결과로 보고한다. ## 왜 필요한가 — 실제 사고 목록 @@ -16,6 +16,8 @@ | 브랜드가 모바일에서 1글자 폭으로 수축, 세로로 쌓임 | 수축 탐지(짧은 라벨 n줄 이상) + 브랜드 1줄 | | 기기 글꼴 확대(안드로이드 큰 글꼴 1.3×)에서 제목 부서짐 | 스케일×폭 h1 행렬 | | 본문 보조색이 배경 대비 4.4:1 (AA 미달) | 전 요소 대비 스캔(블렌드 계산) | +| CSS `color-mix()`의 computed `color(srgb …)`를 0~255 rgb로 오독해 정상 대비를 1:1로 거짓 실패 | CSS Color 4 `color(srgb 0~1)` 파싱 + 알파 합성 RED fixture | +| `.sr-only`·`aria-hidden` 장식·빈 overlay의 범위를 보이는 셀/버튼 글자로 합산해 0px 여백 거짓 실패 | text node 단위 실측 + 비가시 조상 제외. 화면에 보이는 글자만 인셋 판정 | | 조사만 하고 안 고친 영역(관심 과목 등) | 전 뷰 순회 — 검사는 '내가 고친 것'이 아니라 전체 표면 | | 대시보드 26명 ↔ 명단 12명 숫자 모순 | 콘텐츠 정합(요약 = 재계산 비교) | | aria-label 을 div/ul 에 오용(보조기술 무시) | html-validate + axe | @@ -29,12 +31,14 @@ | 같은 명시도의 숨김 규칙이 기본 규칙보다 앞에 있어 짐 — 모바일에서 숨기려던 kbd 안내가 렌더됨(6% 시각 diff) | 시각 회귀 + 규칙: **숨김 오버라이드는 기본 규칙보다 파일 뒤쪽에** 두거나 컴포넌트 규칙 근처의 미디어쿼리로 | | 탐색 차터가 결과를 단언하지 않아 "9288점" 입력이 조용히 통과(값을 덧붙인 타이핑) | 하니스 규칙 8(검사는 단언)·9(number 입력 조작) | | 모바일 라운드 — 중앙 모달이 썸존 밖, 백 키가 앱을 통째로 종료, safe area 무시, 입력 확대 | L6 시나리오(백·시트·터치타깃 스윕) + `references/mobile-app-ux.md` 표준 — **OS 관습은 조사 없이 바꾸지 않는다** | +| 전역 `* { margin: 0 }`가 native dialog의 UA 중앙 여백을 지워 좌상단에 고정, 짧은 화면의 패널은 고정 크롬에 눌려 내부 scroll이 0px | dialog의 `fixed/inset/margin:auto`를 명시하고 390·1440·2560 중심 오차, viewport rect, `html` scroll lock, short-height scroll owner를 hard fail로 측정. 모달 행동은 [WAI-ARIA APG](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/)를 따른다 | +| 2560px 분할 히어로에서 사진이 1400px 이상으로 팽창하고 카피 열은 200px 안팎으로 붕괴 | hero·copy·media·후속 정보 레일의 rect 계약. stage/media/copy/workbench 토큰을 분리하고 media 상한·copy 최소폭·정렬을 hard fail로 측정 | | 탭 전환 스크롤 잔류 — 짧은 뷰에서 클램프되어 헤더 반쯤 잘림, 뷰마다 크롬 위치 제각각 | 뷰 전환 `scrollTo(0,0)` 단언(M5) + mobile-app-ux.md | | 크럼을 헤더로 내보내 빈약한 헤더 — 계측 전부 통과했지만 사용자에게 '깨져 보인다' | 헤더 sticky·브랜드+액션 구조 단언 + **시각 무게 평가(눈)** — mobile-app-ux.md | | 왼쪽 인셋 리듬 불일치(제목 16 vs 앱바·카드 24) — 오버플로 검사는 오른쪽만 보므로 통과 | M6 왼쪽 인셋 스위트(텍스트 시작점 ≥12px·같은 축 ±10px) + preflight §0-E | | 모바일에서 절대 배치 지도 자식의 부모를 `position: static`으로 바꿈 — offsetParent가 BODY가 되어 격자·핀이 히어로 전체를 덮음. 기하 게이트 8/8 통과 | **상태별 실제 캡처를 열어 보는 시각 E2E** + absolute/fixed 자식의 containing block 단언(하니스 규칙 19) | | 새 관리자·가맹 서브페이지가 생겼지만 `public/work/*/index.html`만 검사 — 새 HTML 오류 8건이 검증 밖 | 정적 입력을 `public/work/**/*.html`·`**/*.css`로 재귀화 + 검사 대상 파일 수 단언(하니스 규칙 20) | -| 마케팅 h1의 의도적 2줄 때문에 범용 1줄 규칙을 꺼 버릴 위험 | 기본 1줄 유지 + 페이지별 `h1MaxLines` 계약. 375·390 × 글꼴 확대 매트릭스도 같은 상한 적용 | +| 마케팅 h1의 의도적 2줄 때문에 범용 줄 수 규칙을 강제할 위험 | 페이지별 읽기 폭·문구·글꼴 확대의 계약을 기록하고 375·390 × 글꼴 확대에서 실제 줄 수와 가독성을 판정. 줄 수를 맞추려고 글자 크기를 축소하지 않음 | ## 7계층 @@ -44,7 +48,7 @@ |---|---|---| | L0 정적 | stylelint + html-validate | 문법·구문 위반을 브라우저 켜기 전에 | | L1 단위 | 순수 계산 로직 추출(calc.js) + 경계값 테스트 | 프로젝트별 작성 — 0명/만석/초과/빈배열. **입력 검증·신청 가능 같은 '판정'(가능/불가+사유)도 여기 둔다** — UI 는 판정을 문장으로 번역만 | -| L2 불변식 | `design-gate.mjs` — 오버플로·h1·수축·대비·SEO/meta·죽은 선택자·토큰 위생·theme-color 정합 | 전 뷰 × 전 폭 | +| L2 불변식 | `design-gate.mjs` — 오버플로·제목 존재/가시성·수축·대비·SEO/meta·죽은 선택자·토큰 위생·theme-color 정합. 명시한 양의 `h1MaxLines`만 줄 수 계약으로 검사 | 전 뷰 × 전 폭 | | L3 시각 회귀 | 기준 화면 pixelmatch diff (≥0.1% 실패) + 핵심 상태 캡처 | 갱신은 검증된 배포 후 `--update-baseline`. 캡처 존재와 사람의 시각 판정은 별개다 | | L4 WebKit | Playwright webkit | 사파리 엔진 렌더 차이 | | L5 탐색 | 키보드 완전 통과 + 사용자 여정 차터 | 프로젝트별 작성 — 마우스 금지 | @@ -70,7 +74,7 @@ L1·L5 는 프로젝트 안에 `tools/unit/*.test.mjs`, `tools/exploratory.mjs` - **Windows**: 스크롤바가 오버레이가 아니다 — `overflow-x:auto` 짝에는 `overflow-y:hidden` + `scrollbar-width` 검토. 스크롤바 자체가 레이아웃을 민다. - **iOS/Safari(WebKit)**: `100vh` 주소창 문제(`100dvh`), 텍스트 인플레이션(`-webkit-text-size-adjust:100%`), WebKit 만의 flex/행높이 차이 → L4 로 재검. -- **안드로이드**: 시스템 글꼴 확대(1.15~1.3× 흔함) — rem 전면 확대로 제목 칸 수축 → 스케일×폭 행렬으로 재현·검증. +- **안드로이드**: 시스템 글꼴 확대는 제목·컨트롤을 수축시킬 수 있다. gate의 `fontScales`와 computed root 스케일은 회귀 신호일 뿐, px 고정 서체의 실제 OS 확대를 보장하지 않는다. 실제 접근성 확대와 WCAG 200% zoom은 별도 브라우저·기기 검증으로 보고한다. - **폰트 스왑**: `font-display: swap` 의 CLS — 폴백 메트릭 보정(typography.md §4) 없이 검사 통과를 선언하지 않는다. ## 하니스 규칙 (테스트를 만드는 테스트) @@ -103,12 +107,18 @@ L1·L5 는 프로젝트 안에 `tools/unit/*.test.mjs`, `tools/exploratory.mjs` 26. **진실 라벨은 개수가 아니라 의미 범위를 단언한다** — 같은 상태·출처·행동 가능성을 공유하는 자식은 제목과 경계가 분명한 가장 가까운 공통 부모가 한 번 소유하고 프로그램적으로 연결한다. 상태가 다른 자식만 예외 라벨을 둔다. 같은 공개 문구가 한 그룹의 모든 자식에 복제되면 범위 설계를 다시 본다. 27. **보이는 입력과 판정 의존성을 같은 SSOT로 잠근다** — 폼의 사용자 조건 이름 집합과 순수 판정 함수가 참조하는 사용자 조건 집합을 비교한다. 숨은 기본값을 사용자 선호로 세지 않고, 보이는 입력을 무시하지 않으며, 분석 전 완료 결과가 없고 복합 비용 일부로 적합을 확정하지 않는지 단언한다. 28. **공개와 관리 콘텐츠 소유권을 테스트한다** — 공개 DOM에는 내부 ID·원문 출처 원장·규제 채널 상수·감사 로그를 복제하지 않는다. 대신 사용자 행동을 막거나 바꾸는 제한과 해석 가능한 이유는 남긴다. 관리자는 그 요약을 재현하는 출처·검증 이벤트·판정 기록을 가진다. +29. **색·가시성 측정기는 새 CSS 문법과 보이는 텍스트를 구분한다** — computed color가 CSS Color 4 `color(srgb r g b / a)`이면 0~1 채널을 0~255으로 변환하고, 반투명 배경은 자식에서 부모 방향으로 올바르게 합성한다. `Range.selectNodeContents()`로 sr-only·`aria-hidden` 장식·빈 overlay까지 합산하지 말고 실제 보이는 text node의 rect만 잰다. 두 경우 모두 UI를 고치기 전에 RED fixture → 파서 수정 → 같은 fixture GREEN 순서로 검사기 자체를 검증한다. +30. **native dialog의 위치를 UA 스타일에 맡기지 마라** — 전역 reset은 dialog의 기본 margin을 지울 수 있다. open 상태에서 `(left + width/2, top + height/2)`와 viewport 중심의 오차, 화면 안 rect, `html` overflow, Escape 뒤 복귀를 390·1440·2560에서 측정한다. 내용이 길면 dialog 또는 내부 본문 하나만 scroll owner여야 한다. +31. **초광폭 히어로는 비율 계약을 잰다** — 2560 같은 실제 wide viewport에서 hero 폭/정렬, copy 최소 폭, media 최대 폭, 다음 정보 레일 폭을 함께 단언한다. media만 넓고 카피가 가늘어지는 상태를 `overflow=0`으로 통과시키지 않는다. 절대 수치는 페이지별 토큰이 소유하며, 풀블리드 편집 사진 예외는 이유를 기록한다. +32. **캐시 무효화도 사용자 화면에서 단언한다** — static CSS/JS를 덮어쓴 뒤 HTML만 새 URL이면 브라우저는 이전 stylesheet를 계속 쓴다. stylesheet와 `@import` 의 버전을 같이 바꾸고, 공개 URL에서 실제 href·새 토큰/핵심 computed value를 검사한다. HTTP 200과 새 HTML만으로 배포 성공을 선언하지 않는다. +33. **썸네일은 안정한 프레임만 저장한다** — 캡처 전 local HTTP 경로에서 `document.fonts.ready`·모든 이미지 `decode()`·bounded settle을 기다리고, `prefers-reduced-motion: reduce`를 적용한다. 그 뒤 video/audio·CSS/WAAPI·rAF canvas를 멈추고, local 4xx·pageerror·깨진 이미지가 하나라도 있으면 실패시킨다. 원리는 `animation:none`으로 초기 상태를 재현하는 것이 아니라 **완성된 한 프레임을 고정**하는 것이다. 카드·OG·핵심 상태가 같은 계약을 공유하는지 파일 치수·0바이트·시각 검수까지 단언한다. +34. **초광폭 text-media split은 가족별로 잰다** — hero가 통과했다고 과정·추천·가맹 분할이 통과한 것이 아니다. 서로 다른 DOM/여백 규칙을 가진 각 split root에서 1920·2560의 rail, copy/media rect, heading의 실제 내용 폭과 줄 수를 hard fail로 기록한다. wide override는 양쪽 logical padding을 명시하고, 상위 container의 `max-width`가 specificity 때문에 named stage를 다시 줄이지 않는지 단언한다. grid 바깥 rect가 정상이어도 text content가 한 글자 열로 수축하면 실패다. ## 리포트 양식 ``` -게이트: 전 계층 통과 — 배포 가능 · 총 Ns -| 계층 | 결과 | 항목 | 소요 | +게이트: 기능·접근성 자동 검사 / 시각 평가를 별도 보고 · 총 Ns +| 범주 | 결과 | 항목 | 소요 | 근거 | ``` -실패 상세는 원문 테일 포함. 리포트 파일(gate-report.md / verify-report.md)은 커밋 대상이다. +실패 상세는 원문 테일 포함. 시각 평가는 같은 viewport·상태·자산을 전후로 보존해 위계·브랜드·타입·구도·이미지·리듬을 기록한다. 사용자 성과의 인과나 취향 우승을 검증 없이 주장하지 않는다. 리포트 파일(gate-report.md / verify-report.md)은 커밋 대상이다. diff --git a/packages/skill/references/design-foundations.md b/packages/skill/references/design-foundations.md new file mode 100644 index 0000000..75b6d30 --- /dev/null +++ b/packages/skill/references/design-foundations.md @@ -0,0 +1,67 @@ +# design foundations — 근거를 결정으로 바꾸는 법 + +교과서 요약집이 아니다. 각 원칙은 실제 과업에서 무엇을 확인할지 정하는 도구다. 출처의 상세 범위는 [evidence ledger](evidence-ledger.md)를 따른다. + +## 1. 먼저 성공 과업을 한 문장으로 적는다 + +`누가 / 어떤 맥락에서 / 무엇을 / 어떤 위험 없이 끝내야 하는가`를 쓴다. 시각 방향은 그 뒤에 고른다. 첫 화면의 주장은 이름, 가치, 다음 행동을 모두 억지로 넣는 공식이 아니라 이 과업의 다음 결정을 충분히 설명하는지로 판정한다. + +| 원칙 | 지지 주장 | 적용 조건 | 잘못된 일반화 | 구체 검증 | 예시 결정 | +|---|---|---|---|---|---| +| 관련성 | 불필요한 정보는 관련 정보의 가시성과 경쟁한다 [NNG-HEUR] | 첫 선택과 오류 복구가 중요한 화면 | 요소 수가 적을수록 좋다 | 대표 과업에서 필요한 정보가 같은 화면에 있는지 본다 | 가입 전 가격 세부는 요약+상세 링크로 분리 | +| 위계 | 크기·명도·위치·여백은 함께 작동해 시선을 만든다 [NNG-VISUAL] | 스캔이 필요한 랜딩·목록 | h1:h2 비율 하나로 판정 가능 | 회색조 캡처를 짧게 보고 제목·주요 행동·본문의 읽는 순서를 기록한다 | CTA만 색 대비, 섹션 제목은 공간 대비 | +| 근접성·유사성·공통 영역 | 가까움, 공통 외곽, 유사한 표식은 관계를 읽히게 할 수 있다 [GESTALT] | 반복 모듈과 복잡한 정보 | 모든 영역이 12열 중앙 정렬이거나 카드여야 한다 | 카드·표·CTA의 묶음·분리·선택 상태를 회색조와 실제 과업에서 본다 | 사양과 가격은 한 group, 관련 도움말은 다음 group | +| 정렬과 그리드 | 일관된 column·gutter·breakpoint는 제품 UI의 정렬 기준을 제공한다 [IBM-GRID] | 여러 화면의 반복 표면 | Carbon 격자나 특정 열 수가 보편 정답이다 | 콘텐츠 폭, 정렬선, 작은 폭의 재배치를 검증한다 | 읽는 surface와 비교 surface에 다른 폭 토큰을 둔다 | +| 직접 조작 | 멀수록 작을수록 조준이 느리고 오류가 늘 수 있다 [FITTS] | 빈번한 포인터 행동 | 모든 버튼을 크게 만들면 해결된다 | 실제 hitbox, 거리, 인접 오탭을 관찰한다 | 아이콘만 누르게 하지 않고 라벨까지 클릭 가능하게 | +| 단서와 선택 설계 | 행동 단서는 지각 가능해야 하고, 선택 비용은 선택지 수만 아니라 구분·라벨·목표에 좌우된다 [NORMAN-SIGN, HICK, KRUG] | 내비·필터·설정 | 7±2로 메뉴 수를 제한한다 | 사용자가 목표 항목을 찾는 시간·오류를 본다 | 9개 항목을 사용자 과업별 3개 묶음으로 재분류 | +| 기억보다 인지 | 화면의 상태·다음 행동·되돌림을 드러내면 기억 부담을 낮춘다 [NNG-HEUR, MILLER] | 다단계·파괴적 행동 | 모든 것을 항상 보여야 한다 | 뒤로가기·오류·재진입에서 상태가 복구되는지 시험한다 | 업로드 후 파일명·교체·삭제를 같은 표면에 노출 | +| 인지 부하 | 과업에 필요 없는 기억·변환·탐색 단계를 줄이면 문제 해결 부담을 낮출 수 있다 [COGNITIVE-LOAD] | 낯선 절차·설정·비교 | 모든 정보를 숨기거나 단계를 줄이면 더 쉽다 | 첫 사용자 과업의 오류·되돌림·완료 시간을 비교한다 | 설정 값을 군집화하고 선택 결과를 바로 옆에 보인다 | + +## 2. 접근성은 시각 스타일과 독립된 통과선이다 + +WCAG는 A·AA·AAA의 테스트 가능한 성공 기준이다. 제품의 목표 수준과 적용 법규·계약을 먼저 정하고, 장식적 선택이 그 기준을 넘어서는지 확인한다. AA는 일반적인 목표가 될 수 있지만, 이것만으로 모두에게 적합하다는 보증은 아니다 [WCAG-22]. + +- 텍스트 대비, 200% 확대, 320 CSS px reflow, 사용자 text-spacing, 포인터 target, 자동 움직임을 핵심 화면·상태별로 시험한다 [WCAG-READ, WCAG-TARGET, WCAG-MOTION]. +- 모션 감소 선호에서는 움직임을 줄이되, 로딩·선택·오류 같은 상태 신호를 다른 방식으로 남긴다 [W3C-REDUCED]. +- 스크린샷은 이 기준을 증명하지 못한다. 키보드·줌·사용자 스타일·보조기술 테스트를 별도 기록한다. + +## 3. 성능 수치의 이름을 바르게 쓴다 + +Core Web Vitals 적합성은 실제 사용자의 field data에서 모바일·데스크톱별 75th percentile로 판단한다. 로컬 Lighthouse/DevTools/테스트 장비 수치는 개발 회귀를 잡는 lab proxy이며 field 판정이 아니다 [CWV, CWV-METHOD]. + +| 주장 | 필요한 증거 | +|---|---| +| “개발 빌드가 이전보다 가벼워졌다” | 같은 환경·시나리오의 bundle/전송량 또는 lab 결과 | +| “CWV good” | RUM 또는 CrUX의 LCP·INP·CLS p75, 기간·트래픽 분리 | +| “사용자가 더 빨리 끝낸다” | 실제 과업 시간·성공률·오류/중단의 전후 비교 | +| “이 디자인이 더 아름답다” | 동일 브리프에서의 렌더 비교와 근거 있는 리뷰; 자동 점수로 단정하지 않음 | + +## 4. 비교는 같은 조건에서 한다 + +브랜드 고유성, 시각 위계, 구도·리듬, 이미지 품질은 실제 렌더에서 비교한다. 평가자는 같은 브리프·폭·상태·콘텐츠에서 다음을 적는다: 무엇이 첫 번째로 읽히는가, 어떤 근거가 이 브랜드에만 맞는가, 반복이 정보 구조를 드러내는가, 이미지의 크롭·해상도·대체 텍스트가 목적을 지지하는가. 심미성 자동점수, 상 수상, 단일 클릭 수로 전환 인과를 주장하지 않는다 [AWWWARDS, WEBBY]. + +## 5. 필요할 때: 평가의 네 증거선을 분리한다 + +예쁘다는 평가와 실제 과업 결과가 엇갈리거나, 변경이 개선 효과를 냈다고 주장하려 할 때 이 절을 연다. 평가 종류가 답하는 질문은 다르다. 첫인상·취향, 실제 과업 행동, 접근성 적합성, 전문가의 시각 검토를 한 PASS로 합치지 않는다. 보기 좋은 표면이 사용성 평가를 좋게 느끼게 할 수 있으므로, 호감과 과업 결과도 따로 적는다 [NNG-AESTHETIC]. + +| 증거선 | 답하는 질문 | 최소 기록 | 다른 증거로 대체할 수 없는 것 | +|---|---|---|---| +| 첫인상·취향 | 무엇이 먼저 읽히고 어떤 인상을 주는가 | 같은 렌더 조건, 관찰, 해석 | 실제 과업 성공·접근성 적합성 | +| 실제 과업 행동 | 사람이 목표를 달성하며 어디에서 멈추거나 오류를 내는가 | 현실적인 중립 과업, 행동·발화·막힌 지점 | 표준 적합성·전문가 미감 판정 | +| 접근성 적합성 | 정한 WCAG 수준과 제품 계약을 충족하는가 | 기준, 상태, 키보드·확대·보조기술 결과 | 사용자 경험 전체 | +| 전문가 시각 검토 | 위계·타입·구도·이미지·리듬이 브리프와 맞는가 | 같은 조건의 전후 렌더, 관찰, 변경 근거 | 실제 사용자 연구 | + +불확실하거나 영향이 큰 과업이면 실제 또는 예상 사용자가 현실적인 목표를 중립적 과제로 수행하게 하고, 답을 암시하지 않은 채 행동을 관찰한다 [GOV-TEST]. 실제 사용자 검증이 없으면 그 한계를 결과에 적고, 과업 효과를 확정적으로 주장하지 않는다. 사용자 평가는 표준 적합성 평가를 보완하며 대체하지 않는다 [W3C-USER-EVAL]. 일반적인 구현에 사용자 모집을 필수 게이트로 만들지 않는다. 에이전트 walkthrough는 정해진 시나리오의 회귀 점검일 뿐 실제 사용자 연구가 아니다. + +수정은 `관찰 → 원인 가설 → 한 가지 변경 → 같은 조건의 재비교`로 남긴다. 필요하면 여러 구조적 대안을 탐색하고 작은 범위에서 시험해 버리거나 다듬는다는 Double Diamond의 프레임을 참고한다 [DC-DIAMOND]. 이 절차는 출처가 보장한 단일 방법이 아니라, 위 근거를 스킬 작업에 맞게 합성한 절차다. 고정된 관찰 시간·참여자 수·성공률 하나를 보편 통과선으로 쓰지 않는다. + +## 6. 결정 기록 템플릿 + +```markdown +- 출처 ID: WCAG-TARGET +- 지지 주장: 포인터 대상의 AA 최소 크기는 24×24 CSS px이다. +- 적용 조건: 모바일에서 빈번한 아이콘 행동. +- 잘못된 일반화: 모든 컨트롤에 44px가 법적 필수라는 말. +- 구체 검증: 390px에서 실제 클릭 영역과 인접 대상 간격을 측정한다. +- 예시 결정: 삭제 아이콘의 보이는 glyph는 16px, 클릭 영역은 28px로 둔다. +``` diff --git a/packages/skill/references/evidence-ledger.md b/packages/skill/references/evidence-ledger.md new file mode 100644 index 0000000..dadde5a --- /dev/null +++ b/packages/skill/references/evidence-ledger.md @@ -0,0 +1,58 @@ +# evidence ledger — 판단의 출처와 한계 + +조사일: 2026-09-12. 이 목록은 읽을거리 순위가 아니라, 디자인 결정에 쓸 수 있는 근거의 범위다. `normative`는 적합성 조건, `research`는 특정 인간-컴퓨터 상호작용의 경향, `practice`는 설계 절차, `case`는 한 사례 또는 심사 체계, `forecast`는 전망, `bibliography`는 서지 확인만 뜻한다. 사례·전망·서지는 효과나 인과를 증명하지 않는다. + +| ID | 출처·날짜 | 종류 | 실제로 지지하는 주장 | 적용 조건 / 잘못된 일반화 | 구체 검증 | +|---|---|---|---|---|---| +| WCAG-22 | [WCAG 2.2](https://www.w3.org/TR/WCAG22/) · 2023-10-05 | normative | A·AA·AAA는 서로 다른 적합성 수준이며 AAA만으로 모두에게 접근 가능해지지는 않는다 | 법·계약 목표는 별도로 확인한다. AA를 자동 법적 보장이라 부르지 않는다 | 요구 수준과 전 과정의 SC를 매핑한다 | +| WCAG-READ | [contrast](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html) · [resize](https://www.w3.org/WAI/WCAG22/Understanding/resize-text.html) · [reflow](https://www.w3.org/WAI/WCAG22/Understanding/reflow.html) · [text spacing](https://www.w3.org/WAI/WCAG22/Understanding/text-spacing.html) · 2023 | normative | 일반 텍스트 AA 4.5:1/큰 텍스트 AA 3:1, 200% 확대, 320 CSS px 재배치, 사용자 지정 간격에서의 내용·기능 보존을 각각 요구한다 | 로고·비활성·2차원 배치 같은 예외가 있으며, 이는 단일 ‘좋은 조판’ 수치를 만들지 않는다 | 실제 상태에서 대비, 200% 확대, 320px/400%, W3C text-spacing 주입을 함께 검사한다 | +| WCAG-TARGET | [SC 2.5.8](https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html) · 2023 | normative | 포인터 입력 대상은 최소 24×24 CSS px(예외 포함)인 AA 기준이 있다 | 44px를 모든 대상의 WCAG AA 요건으로 말하지 않는다 | 실제 clickable box와 인접 대상 간격을 측정한다 | +| WCAG-MOTION | [SC 2.2.2](https://www.w3.org/WAI/WCAG22/Understanding/pause-stop-hide.html) · 2023 | normative | 자동으로 시작해 5초 넘게 병행되는 움직임은 멈춤·숨김·일시정지 제어가 필요할 수 있다 | 모든 모션 금지가 아니다 | 자동 콘텐츠마다 제어와 키보드 접근을 시험한다 | +| W3C-REDUCED | [Media Queries 5](https://www.w3.org/TR/mediaqueries-5/#prefers-reduced-motion) · 확인 2026-09-12 | practice | CSS Media Queries 명세는 `prefers-reduced-motion` 질의를 정의한다 | WCAG 성공 기준이나 모든 모션 정책의 적합성 선언이 아니다 | 시스템 설정에서 핵심 상태 피드백이 남는지 확인한다 | +| W3C-FONT | [CSS Fonts 4](https://www.w3.org/TR/css-fonts-4/#font-display-desc) · 확인 2026-09-12 | practice | CSS Fonts 명세는 `font-display`의 로딩 기간 표시 전략을 정의한다 | WCAG 적합성 요건이 아니며, `swap`이 항상 최선이거나 CLS를 반드시 만든다고 단정하지 않는다 | 느린 네트워크에서 FOUT/FOIT·레이아웃을 캡처한다 | +| W3C-SIZE | [CSS Fonts 5](https://www.w3.org/TR/css-fonts-5/#size-adjust-desc) · 확인 2026-09-12 | practice | CSS Fonts Level 5 명세는 `size-adjust` descriptor를 정의한다 | 문서 상태·브라우저 지원을 별도 확인하며, 임의 백분율을 보편 처방으로 복사하지 않는다 | 대상 문자열·실제 폴백으로 줄바꿈과 높이를 비교한다 | +| NNG-HEUR | [10 usability heuristics](https://www.nngroup.com/articles/ten-usability-heuristics/) · 마지막 검토 2024-01-30 | practice | 최소성은 관련 없는 정보가 관련 정보의 가시성을 경쟁시킨다는 휴리스틱이다 | ‘미니멀=요소 삭제’나 전환 효과의 증명이 아니다 | 핵심 과업별 정보·피드백·복구 가능성을 리뷰한다 | +| NNG-VISUAL | [Principles of visual design](https://www.nngroup.com/articles/principles-visual-design/) · 확인 2026-09-12 | practice | scale, visual hierarchy, balance, contrast, Gestalt principles를 시각 디자인의 검토 차원으로 설명한다 | 특정 크기 비율·색 수·자동 심미성 점수를 처방하지 않는다 | 회색조와 실제 콘텐츠에서 우선순위·묶음·대비를 비교한다 | +| NORMAN-SIGN | [Signifiers, not affordances](https://jnd.org/signifiers-not-affordances/) · 2008 | practice | 사용자가 어디서 어떻게 행동할지 알게 하는 지각 가능한 단서는 signifier로 설명할 수 있다 | 시각적으로 버튼처럼 보이면 이해·접근성이 자동 보장된다는 뜻이 아니다 | 첫 사용자가 라벨·상태·피드백으로 행동을 예측하는지 관찰한다 | +| KRUG | [Don’t Make Me Think Revisited sample](https://sensible.com/downloads/DMMT-Revisited-sample-chapter.pdf) · 확인 2026-09-12 | bibliography | 공개 샘플은 Krug의 저작과 ‘명확한 선택지’라는 교육적 관점을 확인하는 범위다 | 메뉴 수 고정 규칙이나 실험적 성과 증거가 아니다 | 애매한 라벨을 대표 사용자의 과업 탐색으로 시험한다 | +| FITTS | [Fitts’s law](https://doi.org/10.1037/h0055392) · 1954 | research | 목표까지 거리와 이동축의 폭은 조준 시간에 관계한다 | 메뉴 개수 공식이나 모바일 타깃 크기 법규가 아니다 | 자주 쓰는 행동의 실제 거리·hitbox·오입력을 관찰한다 | +| HICK | [Hick](https://doi.org/10.1080/17470215208416600) · 1952 | research | 선택 신호의 정보량과 반응 시간 관계를 실험했다 | ‘7개 이상 메뉴 금지’로 변환하지 않는다 | 사용자 목표별 선택지의 분류·라벨·성공 시간을 테스트한다 | +| MILLER | [Miller](https://doi.org/10.1037/h0043158) · 1956 | research | 단기 기억의 절대적 한계가 아니라 chunking과 과제 맥락을 논의했다 | 7±2를 내비게이션 항목 수 규칙으로 쓰지 않는다 | 회상 대신 과업 완료·오류·도움 요청을 관찰한다 | +| GESTALT | [Wagemans et al.](https://doi.org/10.1037/a0029333) · 2012 | research | 근접성·유사성·공통 영역 같은 grouping 원리와 figure-ground를 현대적으로 검토한다 | 정렬·카드·색 하나만으로 이해·전환이 보장된다는 뜻이 아니다 | 관련 항목의 묶음과 분리, 선택 상태를 회색조·키보드·실제 과업에서 확인한다 | +| COGNITIVE-LOAD | [Sweller](https://onlinelibrary.wiley.com/doi/10.1207/s15516709cog1202_4) · 1988 | research | 문제 해결에서 작업 기억과 문제 구조의 상호작용을 다룬다 | 정보량을 무조건 줄이거나 단일 요소 수 규칙으로 환원하지 않는다 | 처음 하는 과업에서 단계·용어·기억해야 할 상태를 줄이고 오류·완료 시간을 관찰한다 | +| AWWWARDS | [Evaluation system](https://www.awwwards.com/about-evaluation/) · 확인 2026-09-12 | case | 해당 심사는 Design 40 / Usability 30 / Creativity 20 / Content 10으로 채점한다 | 배점 비율이 두 요소의 효과 크기나 사업 성과를 뜻하지 않는다 | 제출 목적일 때 네 차원을 별도 체크한다 | +| WEBBY | [Judging criteria](https://www.webbyawards.com/judging-criteria/) · 확인 2026-09-12 | case | Webby는 콘텐츠·구조/내비게이션·시각 디자인·기능성·상호작용·전반 경험을 심사한다 | 수상은 특정 패턴의 보편적 효율 증거가 아니다 | 사례를 해체하되 과업·접근성·성능을 별도 검증한다 | +| DROPBOX | [Dropbox brand](https://brand.dropbox.com/) · 확인 2026-09-12 | case | 브랜드 사이트가 공개한 자산·원칙의 사례다 | 브랜드의 자체 설명을 사용자 효과로 해석하지 않는다 | 사용할 요소를 렌더·접근성·브리프 적합성으로 다시 판정한다 | +| AREA17 | [AREA 17 × Pentagram](https://area17.com/ideas/area-17-pentagram) · 확인 2026-09-12 | case | 제작사가 프로젝트 접근을 설명하는 사례다 | 제작사 주장은 독립 성과 검증이 아니다 | 구조와 시스템 선택만 참고하고 결과는 직접 비교한다 | +| UNSEEN | [Unseen 2025 wrapped](https://2025.unseen.co/) · 확인 2026-09-12 | case | 시간·문화 맥락이 강한 실험적 웹 표면의 사례다 | 사이트 자체만으로 사용성·전환 효과를 주장하지 않는다 | 모션 off·키보드·모바일에서 핵심 메시지를 검증한다 | +| CSSDA-UNSEEN | [CSS Design Awards listing](https://www.cssdesignawards.com/sites/unseen-studio-2025-wrapped/48957/) · 2026-03-09 | case | CSSDA가 해당 작품을 WOTD로 기록한 사례다 | 단일 수상을 스타일 효과의 일반 증거로 쓰지 않는다 | 작품의 제약과 대상이 현재 브리프와 맞는지 비교한다 | +| CWV | [Web Vitals](https://web.dev/articles/vitals) · 2024-10-31 | practice | CWV의 good 기준은 LCP ≤2.5s, INP ≤200ms, CLS ≤0.1이며 field 75th percentile로 판정한다 | Lighthouse 한 번이나 개발 로컬 측정을 field 적합성이라 부르지 않는다 | RUM/CrUX와 모바일·데스크톱 p75를 기록하고, 로컬은 회귀 proxy로 표기한다 | +| CWV-METHOD | [Threshold methodology](https://web.dev/articles/defining-core-web-vitals-thresholds) · 2025-05-07 | practice | CWV 임계값은 단일 인간 지각값이 아니라 연구·실사용 데이터·달성 가능성을 함께 고려한다 | 한 임계값을 모든 과업의 만족도 원인으로 단정하지 않는다 | 성능 수치와 과업 관찰을 분리 보고한다 | +| IBM-GRID | [Carbon 2x Grid](https://carbondesignsystem.com/elements/2x-grid/usage/) · 확인 2026-09-12 | practice | 제품 UI에서 그리드·gutter·breakpoint를 일관되게 적용하는 방법의 예다 | 모든 브랜드가 Carbon 격자를 써야 한다는 규범이 아니다 | 콘텐츠 폭, 정렬선, 작은 폭의 재배치를 검증한다 | +| IBM-DATA | [IBM data visualization basics](https://www.ibm.com/design/language/data-visualization/design/basics/) · 확인 2026-09-12 | practice | 데이터 표현은 목적·비교·맥락에 맞춰 선택해야 한다 | 장식적 차트나 색만으로 의미가 생긴다고 보지 않는다 | 질문-표현-축/라벨-대체 접근을 연결해 검토한다 | +| IFD-TREND | [iF Design Trend Report 2025](https://ifdesign.com/en/if-magazine/newsroom/if-design-trend-report-2025) · 2025 | forecast | 기관의 거시 디자인 전망이다 | 웹 UI의 검증된 성과나 필수 스타일 목록이 아니다 | 브리프·사용자 조사와 맞을 때만 가설로 쓴다 | +| DC-DIAMOND | [Design Council Double Diamond](https://www.designcouncil.org.uk/resources/the-double-diamond/) · 확인 2026-09-12 | practice | 발견·정의·개발·전달을 오가며 다양한 답을 탐색하고 작은 시험으로 버리거나 개선하는 프레임을 소개한다 | 고정 단계 수나 모든 프로젝트의 의무 절차로 쓰지 않는다 | 불확실성이 큰 결정의 대안·시험·폐기 근거를 기록한다 | +| VA-MOFFAT | [Curtis Moffat: working methods](https://www.vam.ac.uk/articles/curtis-moffat-working-methods) · 2025-09-24 | case | contact sheet, 크롭, 비중심 배치가 작가 작업에서 어떻게 쓰였는지 보여 주는 사례다 | 웹 성과나 보편 구도 규칙의 증거가 아니다 | 이미지 선택·크롭의 목적과 전후 렌더를 비교한다 | +| CH-DESCRIPTION | [Cooper Hewitt image-description guidelines](https://www.cooperhewitt.org/cooper-hewitt-guidelines-for-image-description/) · 확인 2026-09-12 | practice | 중요한 내용을 먼저 두고 공간 순서와 맥락에 맞춰 이미지를 설명하는 방법을 제시한다 | 박물관의 단어 수를 보편 alt 길이 규칙으로 바꾸지 않는다 | 정보성 이미지에서 목적·핵심 내용·대체 설명을 함께 검토한다 | +| LUPTON-POSTERS | [How Posters Work](https://www.cooperhewitt.org/publications/how-posters-work/) · 확인 2026-09-12 | bibliography | Lupton의 책과 포스터를 분석 대상으로 삼는 출판 맥락을 확인하는 출발점이다 | 이 항목은 책 본문을 정독했거나 특정 구성 규칙을 인용했다는 뜻이 아니다 | 판본과 페이지를 확보한 뒤 적용할 개념과 한계를 기록한다 | +| GOV-IMAGES | [GOV.UK Design System: images](https://design-system.service.gov.uk/styles/images/) · 확인 2026-09-12 | practice | 정부 서비스에서 목적에 맞는 사진·도식·대체 설명을 선택하는 지침이다 | 상업 브랜드에 무드 이미지를 금지하는 일반 규칙으로 확대하지 않는다 | 이미지가 과업 정보·맥락·접근성에 기여하는지 확인한다 | +| GOV-TEST | [GOV.UK moderated usability testing](https://www.gov.uk/service-manual/user-research/using-moderated-usability-testing) · 확인 2026-09-12 | practice | 현실적인 중립 과업으로 실제 또는 예상 사용자의 행동과 어려움을 관찰하는 방법을 설명한다 | 특정 참여자 수·시간·성공률을 보편 통과선으로 만들지 않는다 | 과업 문구, 사용 환경, 행동·오류·중단 관찰을 분리 기록한다 | +| NNG-AESTHETIC | [Aesthetic-usability effect](https://www.nngroup.com/articles/aesthetic-usability-effect/) · 2024-02-03, 검토 2026-09-01 | practice | 시각적 호감이 사용성에 대한 지각을 높일 수 있어 실제 과업 결과와 구분해 볼 필요를 해설한다 | 원 논문 전문을 읽었거나 호감이 실제 사용성을 보장한다고 주장하지 않는다 | 취향·첫인상과 과업 행동·오류를 별도 결과로 남긴다 | +| W3C-USER-EVAL | [Involving users in evaluating web accessibility](https://www.w3.org/WAI/test-evaluate/involving-users/) · 2024-04-30 | practice | 사용자 평가는 표준 기반 적합성 평가를 보완하며 둘을 함께 써야 한다고 설명한다 | 적은 표본이나 한 번의 평가를 모든 사용자에게 일반화하지 않는다 | WCAG 평가와 사용자 관찰의 질문·결과를 분리해 기록한다 | +| SWISS-NB | [The International Style 1950–1970](https://www.nb.admin.ch/en/the-international-style-1950-1970) · 확인 2026-09-12 | bibliography | 스위스/국제 타이포그래피의 역사·소장 맥락을 소개한다 | 역사 양식을 기능·전환의 보편 처방으로 바꾸지 않는다 | 그리드·타입 선택의 문화적 근거만 명시한다 | +| BRINGHURST | [The Elements of Typographic Style](https://hartleyandmarks.com/Hartley-%26-Marks-Publishers) · 확인 2026-09-12 | bibliography | Bringhurst 4.0의 서지·출판 정보를 확인하는 출발점이다 | 이 ledger는 책 본문을 읽거나 특정 규칙을 인용했다는 뜻이 아니다 | 판본을 확보한 뒤 페이지 단위로 별도 기록한다 | +| LUPTON | [Thinking with Type, 3rd ed.](https://www.chroniclebooks.com/products/thinking-with-type-1) · 2024 | bibliography | Lupton 저작의 3판 출판 정보를 확인하는 출발점이다 | 이 ledger는 본문 요약이나 보편 수치 근거가 아니다 | 판본을 확보한 뒤 개념·예시 페이지를 별도 기록한다 | +| MULLER-BROCKMANN | [Grid systems in graphic design](https://niggli.ch/en/products/rastersysteme-fur-die-visuelle-gestaltung) · 확인 2026-09-12 | bibliography | Müller-Brockmann 저작의 출판사 서지 정보를 확인하는 출발점이다 | 그리드를 모든 과업의 정답으로 만들지 않는다 | 판본을 확보한 뒤 grid 선택의 목적·예외·검증을 분리 기록한다 | + +## 사용할 때 + +결정마다 `출처 ID / 지지 주장 / 적용 조건 / 잘못된 일반화 / 구체 검증 / 예시 결정`을 남긴다. 예: `WCAG-TARGET / 24×24 AA 최소 / 포인터 대상 / 44px 의무라고 확대하지 않음 / 실제 버튼 box 측정 / 작은 아이콘을 24px 이상 hit area로 확장`. + +## 교과서 입문·정독 범위 + +다음은 **서지 확인된 읽기 경로**다. 이 목록은 본문을 읽었다거나 특정 페이지의 규칙을 채택했다는 주장이 아니다. 입문에서는 편집·그리드·글자 형태의 어휘를 얻고, 실제 프로젝트에 인용할 때는 확보한 판본과 페이지를 다시 기록한다. + +1. Bringhurst, *The Elements of Typographic Style* — 조판 어휘와 본문 리듬의 정독 출발점 [BRINGHURST]. +2. Lupton, *Thinking with Type*, 3rd ed. — 타입을 구성·위계·이미지와 연결하는 입문 [LUPTON]. +3. Müller-Brockmann, *Grid Systems in Graphic Design* — grid의 역사적·실무적 어휘 [MULLER-BROCKMANN]. +4. Meggs 계열의 그래픽 디자인 역사 저작은 별도 판본 서지와 페이지 근거를 확보한 뒤 역사 맥락용으로 추가한다. 이 ledger에서는 미확인 판본의 내용을 인용하지 않는다. diff --git a/packages/skill/references/galleries.md b/packages/skill/references/galleries.md index f6b103f..0b89218 100644 --- a/packages/skill/references/galleries.md +++ b/packages/skill/references/galleries.md @@ -6,7 +6,7 @@ ## 1. 브리프 → 갤러리 라우팅 -`R1`은 구조, `R2`는 톤, `R3`는 디테일을 가져올 소스다. R2는 **반드시 R1과 다른 업종**에서 고른다. +`R1`은 구조, `R2`는 톤, `R3`는 디테일을 검토할 슬롯이다. R2는 다른 업종 또는 매체를 우선 검토하되, 같은 업종이 브리프·시장·제작 조건에 맞으면 그 이유를 기록한다. > **두 행에 동시에 걸리면 청중을 기준으로 고른다.** "개발자용 API 모니터링 SaaS"는 > `SaaS · B2B` 와 `개발자 도구` 양쪽에 해당한다. 보는 사람이 엔지니어면 개발자 도구 행이다. @@ -49,10 +49,7 @@ ## 2. 경고 — 갤러리마다 편향이 있다 -**Awwwards 구조를 SaaS에 이식하지 마라.** -2026 Q1 SOTD 47건의 업종 분포는 럭셔리 패션 28% · 자동차 22% · 건축 18% · 문화기관 14%, **테크/SaaS는 5% 미만**이다. -수상작의 전면 3D·풀스크린 인트로·스크롤 서사는 "제품을 팔지 않는 사이트"의 문법이다. -Awwwards는 **타이포 완성도와 야심의 상한선**으로만 쓰고, 섹션 구조는 Land-book·Refero에서 가져온다. +**Awwwards 사례를 제품 구조의 보편 답으로 쓰지 마라.** 어워드 사례는 심사 맥락·예산·제작 환경이 다르다. 전면 3D·풀스크린 인트로·스크롤 서사는 목적·성능 예산·사용 과업에 맞을 때만 선택한다. 각 사례에서 가져올 원칙과 적용 조건을 기록하고, 정보 구조는 목표 사용자·과업·프로젝트 계약으로 다시 검증한다. 그 밖의 편향: @@ -96,7 +93,7 @@ Awwwards는 **타이포 완성도와 야심의 상한선**으로만 쓰고, 섹 라우팅 표에서 못 찾았을 때만 본다. **종합 어워드** -- Awwwards https://www.awwwards.com — SOTD 아카이브. 상한선용 +- Awwwards https://www.awwwards.com — SOTD 아카이브. 심사 맥락과 적용 조건을 함께 검토 - The FWA https://thefwa.com — 인터랙티브·기술 실험 - Godly https://godly.website — 주당 3~5개만 등록. 신호 대 잡음비 최고 diff --git a/packages/skill/references/images.md b/packages/skill/references/images.md index 63c4379..52ef534 100644 --- a/packages/skill/references/images.md +++ b/packages/skill/references/images.md @@ -8,22 +8,28 @@ --- -## 1. 조달 경로 — 위에서부터 확인한다 +## 1. 조달 경로 — 기본 탐색 순서로 확인한다 | 순위 | 경로 | 언제 | |---|---|---| -| 1 | **사용자가 준 사진** | 있으면 무조건 이것. 실제 작업물을 이길 생성물은 없다 | -| 2 | **생성**(codex `image_gen`) | 콘셉트 확인용, 톤이 정확히 지정된 추상·분위기 컷 | +| 1 | **사용자가 준 사진** | 자산 용도·품질·권리·진실성에 맞을 때. 실제 작업물은 그 근거가 필요한 화면에서 강하다 | +| 2 | **생성**(codex `image_gen`) | 필요한 슬롯에 콘셉트 확인용 또는 톤이 정확히 지정된 추상·분위기 컷이 맞을 때 | | 3 | **스톡** | 라이선스가 명확한 곳만. 출처를 `design.md` 에 적는다 | | 4 | 없이 간다 | 타이포·색·여백만으로 만든다. 약한 사진보다 낫다 | -**0단계에서 물어라**: "쓸 수 있는 사진이 있나요?" -없다고 하면 2번으로 가되, **생성물이라는 사실을 `design.md` 에 적는다.** -나중에 실제 사진으로 바꿀 때 레이아웃을 다시 짜지 않도록 비율을 미리 고정해 둔다. +이 순위는 기본 탐색 순서일 뿐이다. 자산 용도·품질·권리·진실성 판단보다 앞서지 않는다. + +사용자가 이미 자산을 제공했거나 사용 가능한 사진이 없다고 명시했거나, `design.md`·저장소에서 확인할 수 있으면 다시 묻지 않는다. 기존 정보로 확인할 수 없고 자산 유무가 결정에 필요할 때만 **0단계에서 묻는다**: "쓸 수 있는 사진이 있나요?" +없다는 답만으로 생성 경로를 자동 선택하지 않는다. 슬롯별 자산 용도·품질·권리·진실성을 비교해 경로를 고르고, 생성물을 쓰면 **생성물이라는 사실을 `design.md` 에 적는다.** +나중에 실제 사진으로 바꿀 가능성이 있으면 레이아웃을 다시 짜지 않도록 비율을 미리 고정해 둔다. 구도·크롭·타이포 검토는 [art-direction.md](art-direction.md)를 따른다. + +### 제품을 고르는 화면의 추가 원칙 + +사용자가 제품 자체를 비교·선택하는 카드라면 CSS 도형이나 레이어 쌓기로 제품을 대신 그리지 않는다. 실제·라이선스 사진이 없을 때만 비율을 정한 생성 사진을 쓰고, 사진과 같은 시각 그룹에 `생성 이미지`처럼 가까운 출처 라벨을 둔다. DOM 도식은 재료 비교, 과정 설명, 수치처럼 **그림보다 읽히는 정보**에만 쓴다. --- -## 2. codex 가 있으면 생성한다 +## 2. 생성이 적합할 때 사용할 경로 설치 여부를 먼저 확인한다. 없으면 조용히 3번으로 내려간다. diff --git a/packages/skill/references/layout.md b/packages/skill/references/layout.md index 11b9933..1f7e4d5 100644 --- a/packages/skill/references/layout.md +++ b/packages/skill/references/layout.md @@ -25,7 +25,7 @@ **`main` 폭에 고정값을 쓰지 마라.** `--measure` 는 3단계에서 언어와 폰트에 맞춰 정한 값이다. 여기에 60rem 같은 숫자를 넣으면 본문이 measure 를 넘어가 이 문서의 통과 조건을 스스로 어긴다. -한글은 보통 라틴보다 좁다(35ch ≈ 561px vs 640px). +`ch`는 선택한 글꼴의 메트릭에 의존한다. 한글과 라틴의 실제 줄 길이는 글꼴·언어·내용으로 달라지므로, 예시 폭을 보편 수치로 쓰지 말고 대표 문단을 렌더해 확인한다. 이 한 번의 정의로 **본문 폭 · 넓은 블록 · 전체 폭**이 전부 정렬된다. 섹션마다 `max-width` 와 `padding` 을 다시 쓰면 반드시 어긋난다. @@ -39,7 +39,7 @@ ### 벤토 그리드 주의 -벤토(크기가 다른 카드들의 격자)는 **더 이상 차별화가 아니라 새 기본값**이다. 효과는 있지만(스크롤 깊이 증가) 표준화가 끝나서 "고민 안 했음"의 신호로 읽힌다. 쓰려면 최소한 다음 중 하나는 해라: +벤토(크기가 다른 카드들의 격자)는 정보를 묶거나 우선순위를 드러낼 때 쓸 수 있다. 카드 크기 변화만으로 고유성·스크롤 깊이·이탈을 예측할 수는 없다. 아래 장치는 선택지이며, 구조가 필요로 할 때만 쓴다: - 카드 경계를 없애고 여백만으로 구획 - 그리드에서 한 칸을 의도적으로 비우기 - 카드 하나만 규칙을 깨고 밖으로 나가기 @@ -50,10 +50,10 @@ 사람은 페이지를 읽지 않고 **훑는다.** 훑는 경로를 설계하는 것이 레이아웃의 본체다. -1. **첫 화면에서 세 가지만** — 무엇인지 / 왜 좋은지 / 다음 행동. 넷 이상이면 아무것도 안 읽힌다 -2. **위계는 크기가 아니라 대비로 만든다** — 크기·굵기·색·여백 중 **둘 이상을 동시에** 바꿔야 위계가 보인다. 크기만 조금 키우는 것은 위계가 아니다 +1. 첫 화면이 대표 과업에 필요한 정체성·가치·다음 행동을 충분히 설명하는지 확인한다. 세 요소는 흔한 점검 틀이지 모든 화면의 최대 항목 수가 아니다 +2. 위계는 크기·굵기·색·여백·위치의 조합으로 만든다. 어떤 두 변수를 반드시 바꿔야 한다는 공식보다, 회색조와 실제 콘텐츠에서 우선순위가 읽히는지를 본다 3. **시선을 한 번은 멈춰라** — 전부 같은 리듬으로 흐르면 아무것도 강조되지 않는다. 섹션 하나는 리듬을 깨야 한다 -4. **스크롤은 보상이어야 한다** — 다음 화면에 무언가 새로운 것이 있어야 한다. 같은 카드 그리드가 세 번 반복되면 거기서 이탈한다 +4. 스크롤 뒤에도 새 정보나 과업 진전이 있는지 확인한다. 반복 카드가 이탈을 만든다는 보편 증거는 없으므로, 실제 콘텐츠와 사용자 행동으로 판단한다 ### 의미가 다르면 섹션 토폴로지도 달라야 한다 @@ -75,10 +75,10 @@ 한 액션 묶음의 **같은 행에 놓인 직접 자식**인 채운 버튼과 텍스트 링크는 `align-items:center`만으로 시각 중심이 맞지 않을 수 있다. 중첩된 아이콘·배지까지 한꺼번에 비교하거나, 모바일에서 세로로 쌓인 액션에 이 규칙을 적용하면 오탐이다. -- 두 액션 모두 `inline-flex; align-items:center; min-height:44px`의 실제 hitbox를 가진다 +- 두 액션의 실제 hitbox와 정렬을 함께 측정한다. 44px은 편안한 시작값일 수 있지만, WCAG 2.2 AA의 포인터 target 최소 기준은 예외를 포함해 24×24 CSS px다 (`evidence-ledger.md`의 `WCAG-TARGET`) - `getBoundingClientRect()`의 중심 y 차이가 브라우저 CSS 픽셀 기준 2px 이하여야 한다 - 텍스트 baseline이 아니라 클릭 가능한 박스 전체를 비교한다 -- 보조 링크를 44px로 키우면서 빈 padding이 과해지면 액션을 세로로 쌓는다 +- 보조 링크의 hitbox를 키운 결과 밀도가 나빠지면, 행동 중요도·오입력·레이아웃을 보고 세로 배치나 다른 구조를 검토한다 ### 패널을 숨기면 그리드 트랙도 회수한다 @@ -104,7 +104,7 @@ ### 본문이 주인공이다 헤드라인은 눈에 띄기 쉽다. **본문이 읽히는지**가 실력이다. -- 한 줄 길이(`--measure`)를 반드시 적용해라. 전체 폭 본문은 읽을 수 없다 +- 한 줄 길이(`--measure`)를 본문 역할에 적용하고, 실제 언어·글꼴·확대 상태에서 읽기 리듬을 검토한다. 전체 폭이 항상 실패인 것은 아니지만 긴 산문에 무심코 적용하지 않는다 - 문단 간격은 줄간격의 1.5배 이상. 들여쓰기와 문단 간격을 동시에 쓰지 마라 - 링크는 색만으로 구분하지 마라(밑줄 또는 다른 신호 병행) @@ -112,7 +112,7 @@ `references/antipatterns.md` 의 한글 조판 섹션을 읽어라. 요약: - `word-break: keep-all` — 없으면 단어가 아무 데서나 잘린다 - line-height 1.6~1.8 (라틴보다 넉넉하게) -- 음수 자간 금지 +- 자간은 글꼴·크기·스크립트별로 실제 렌더를 보고 조정한다. 한글 음수 자간은 기본 처방으로 쓰지 않으며, 적용했다면 작은 크기·확대·줄바꿈에서 검증한다 - 한 줄 25~40자 - 라틴 폰트를 폴백 스택 **앞**에 둔다 (숫자·영문이 한글 폰트로 렌더되면 조악해진다) - **한글 폰트를 지정하지 않는 것 자체가 완성도 미달 신호다** @@ -131,31 +131,28 @@ .cards { grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); } ``` -`auto-fit` + `minmax` 로 해결되는 것을 미디어쿼리로 만들지 마라. 미디어쿼리는 **레이아웃 구조 자체가 바뀔 때만** 쓴다. +`auto-fit` + `minmax`는 유동 반복 grid에 유용하다. 미디어쿼리와 container query는 구조·밀도·행동이 바뀌는 실제 실패 지점에서 선택한다. ### 모바일에서 먼저 확인해라 데스크톱에서 아름다운 것이 모바일에서 무너지는 것이 기본이고, 그 반대는 드물다. 특히: -- 큰 타이포는 모바일에서 반드시 줄여라 (`clamp` 의 최소값) +- 큰 타이포는 모바일에서 줄 수·가려짐·다음 행동의 가시성을 보고 크기·폭·문구·구도를 함께 조정한다. 단순 축소가 유일한 해법은 아니다 - 가로 스크롤이 생기는 요소를 찾아라 (`overflow-x: hidden` 으로 덮지 말고 원인을 고쳐라) -- 터치 타깃 44×44px 이상 +- 터치 타깃은 과업 빈도·오입력 위험과 WCAG 2.2 AA의 24×24 CSS px 최소 기준(예외 포함)을 함께 검토한다 - 호버로만 접근되는 기능을 만들지 마라 --- ## 4-b. 모바일 내비 — 햄버거를 기본값으로 삼지 마라 -내비 항목 수는 0단계에서 이미 정해져 있다(= 섹션 수). 구현하다 발견하면 늦다. +내비 구조는 섹션 수가 아니라 대표 과업, 라벨 길이, 우선순위, locale, 화면 폭을 함께 보고 0단계에서 정한다. Hick의 연구를 항목 수 공식으로 쓰지 않는다. -| 항목 수 | 처리 | 비용 | +| 관찰한 조건 | 검토할 구조 | 검증 | |---|---|---| -| **2~4개** | 한 줄 유지. 자간을 줄이고 `space-between` 으로 편다 | 0 | -| **5~6개** | 가로 스크롤 줄, 또는 하단 고정 바 | 낮음 | -| **7개 이상** | 여는 메뉴 | `
` 로 JS 0바이트 | +| 모든 우선 행동이 한 줄에서 읽히고 hitbox가 확보됨 | 직접 노출된 내비 | 작은 폭·키보드에서 라벨 접힘과 순서를 본다 | +| 라벨이 길거나 행동 우선순위가 갈림 | 일부 노출 + 추가 메뉴 또는 하단 행동 | 자주 쓰는 행동의 탐색 시간·오입력을 본다 | +| 문맥별 명령이 많음 | 컨텍스트 메뉴·검색·계층적 IA | 메뉴 열기/닫기·포커스·Esc·현재 위치를 시험한다 | -**넷인데 햄버거를 쓰면 탭 한 번을 공짜로 뺏는 것이다.** 반대로 여섯을 한 줄에 -욱여넣으면 접힌다 — 실측에서 링크가 두 줄(top 46/77)에 걸렸고, 워드마크까지 3줄이 됐다. - -### 2~4개 — 쌓는 것이 접히는 것보다 낫다 +### 항목 수 대신 실제 라벨·과업으로 결정한다 워드마크와 내비를 한 줄에 두면 둘 다 접힌다. 좁은 화면에서는 **위아래로 쌓아라.** @@ -169,10 +166,9 @@ 라벨 자간(`0.14em`)이 좁은 화면에서 폭을 크게 먹는다. **자간부터 줄여라** — 폰트 크기를 줄이는 것보다 읽기에 덜 해롭다. -### 7개 이상 — `
` 면 JS 가 필요 없다 +### 단순 공개/접기에는 `
`를 검토한다 -키보드 조작·`Esc`·포커스 이동이 브라우저 기본으로 온다. -직접 만든 토글은 그걸 전부 다시 구현해야 하고, 대개 빠뜨린다. +`
`는 간단한 공개/접기에는 유용하지만, 복잡한 메뉴의 포커스 이동·Esc·모달 동작을 자동으로 완성하지 않는다. 실제 키보드와 보조기술에서 목표 행동을 시험하고, 필요한 패턴을 선택한다. ```html